merge branch 'devel' of github.com:quintusdias/glymur into devel
This commit is contained in:
commit
39cc8a845a
15 changed files with 302 additions and 278 deletions
|
|
@ -11,6 +11,7 @@ import warnings
|
||||||
|
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
|
|
||||||
def xml(raw_data):
|
def xml(raw_data):
|
||||||
"""
|
"""
|
||||||
XMP data to be parsed as XML.
|
XMP data to be parsed as XML.
|
||||||
|
|
@ -23,6 +24,7 @@ def xml(raw_data):
|
||||||
|
|
||||||
return ET.ElementTree(elt)
|
return ET.ElementTree(elt)
|
||||||
|
|
||||||
|
|
||||||
def tiff_header(read_buffer):
|
def tiff_header(read_buffer):
|
||||||
"""
|
"""
|
||||||
Interpret the uuid raw data as a tiff header.
|
Interpret the uuid raw data as a tiff header.
|
||||||
|
|
@ -37,8 +39,8 @@ def tiff_header(read_buffer):
|
||||||
# big endian
|
# big endian
|
||||||
endian = '>'
|
endian = '>'
|
||||||
else:
|
else:
|
||||||
msg = "The byte order indication in the TIFF header ({0}) is invalid. "
|
msg = "The byte order indication in the TIFF header ({0}) is "
|
||||||
msg += "It should be either {1} or {2}."
|
msg += "invalid. It should be either {1} or {2}."
|
||||||
msg = msg.format(read_buffer[6:8], bytes([73, 73]), bytes([77, 77]))
|
msg = msg.format(read_buffer[6:8], bytes([73, 73]), bytes([77, 77]))
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
|
@ -503,6 +505,3 @@ class _ExifInteroperabilityIfd(_Ifd):
|
||||||
def __init__(self, endian, read_buffer, offset):
|
def __init__(self, endian, read_buffer, offset):
|
||||||
_Ifd.__init__(self, endian, read_buffer, offset)
|
_Ifd.__init__(self, endian, read_buffer, offset)
|
||||||
self.post_process(self.tagnum2name)
|
self.post_process(self.tagnum2name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,10 @@ import warnings
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from .core import (
|
from .core import (LRCP, RLCP, RPCL, PCRL, CPRL,
|
||||||
LRCP, RLCP, RPCL, PCRL, CPRL,
|
WAVELET_XFORM_9X7_IRREVERSIBLE,
|
||||||
WAVELET_XFORM_9X7_IRREVERSIBLE, WAVELET_XFORM_5X3_REVERSIBLE,
|
WAVELET_XFORM_5X3_REVERSIBLE,
|
||||||
_Keydefaultdict
|
_Keydefaultdict)
|
||||||
)
|
|
||||||
from .lib import openjp2 as opj2
|
from .lib import openjp2 as opj2
|
||||||
|
|
||||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||||
|
|
@ -57,7 +56,7 @@ _CAPABILITIES_DISPLAY = _Keydefaultdict(_factory,
|
||||||
_PROFILE_0: '0',
|
_PROFILE_0: '0',
|
||||||
_PROFILE_1: '1',
|
_PROFILE_1: '1',
|
||||||
_PROFILE_3: 'Cinema 2K',
|
_PROFILE_3: 'Cinema 2K',
|
||||||
_PROFILE_4: 'Cinema 4K'} )
|
_PROFILE_4: 'Cinema 4K'})
|
||||||
|
|
||||||
# Need a catch-all list of valid markers.
|
# Need a catch-all list of valid markers.
|
||||||
# See table A-1 in ISO/IEC FCD15444-1.
|
# See table A-1 in ISO/IEC FCD15444-1.
|
||||||
|
|
@ -703,7 +702,6 @@ class Codestream(object):
|
||||||
msg = "Invalid number of tiles ({0}).".format(numtiles)
|
msg = "Invalid number of tiles ({0}).".format(numtiles)
|
||||||
warnings.warn(msg)
|
warnings.warn(msg)
|
||||||
|
|
||||||
|
|
||||||
kwargs = {'rsiz': rsiz,
|
kwargs = {'rsiz': rsiz,
|
||||||
'xysiz': xysiz,
|
'xysiz': xysiz,
|
||||||
'xyosiz': xyosiz,
|
'xyosiz': xyosiz,
|
||||||
|
|
@ -1614,6 +1612,7 @@ class SOCsegment(Segment):
|
||||||
msg = "glymur.codestream.SOCsegment()"
|
msg = "glymur.codestream.SOCsegment()"
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
class SODsegment(Segment):
|
class SODsegment(Segment):
|
||||||
"""Container for Start of Data (SOD) segment information.
|
"""Container for Start of Data (SOD) segment information.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,28 +8,29 @@ import warnings
|
||||||
|
|
||||||
from . import Jp2k, set_printoptions, lib
|
from . import Jp2k, set_printoptions, lib
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
"""
|
||||||
Entry point for console script jp2dump.
|
Entry point for console script jp2dump.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
description='Print JPEG2000 metadata.'
|
description = 'Print JPEG2000 metadata.'
|
||||||
parser = argparse.ArgumentParser(description=description)
|
parser = argparse.ArgumentParser(description=description)
|
||||||
|
|
||||||
parser.add_argument('-x', '--noxml',
|
parser.add_argument('-x', '--noxml',
|
||||||
help='Suppress XML.',
|
help='Suppress XML.',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
parser.add_argument('-s', '--short',
|
parser.add_argument('-s', '--short',
|
||||||
help='Only print box id, offset, and length.',
|
help='Only print box id, offset, and length.',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
|
|
||||||
chelp = 'Level of codestream information. 0 suppressed all details, '
|
chelp = 'Level of codestream information. 0 suppressed all details, '
|
||||||
chelp += '1 prints headers, 2 prints the full codestream'
|
chelp += '1 prints headers, 2 prints the full codestream'
|
||||||
parser.add_argument('-c', '--codestream',
|
parser.add_argument('-c', '--codestream',
|
||||||
help=chelp,
|
help=chelp,
|
||||||
nargs=1,
|
nargs=1,
|
||||||
type=int,
|
type=int,
|
||||||
default=[0])
|
default=[0])
|
||||||
|
|
||||||
parser.add_argument('filename')
|
parser.add_argument('filename')
|
||||||
|
|
||||||
|
|
@ -58,7 +59,8 @@ def main():
|
||||||
# JP2 metadata can be extensive, so don't print any warnings until we
|
# JP2 metadata can be extensive, so don't print any warnings until we
|
||||||
# are done with the metadata.
|
# are done with the metadata.
|
||||||
jp2 = Jp2k(filename)
|
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)))
|
print('File: {0}'.format(os.path.basename(filename)))
|
||||||
elif print_full_codestream:
|
elif print_full_codestream:
|
||||||
print(jp2.get_codestream(header_only=False))
|
print(jp2.get_codestream(header_only=False))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import collections
|
||||||
import copy
|
import copy
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
|
|
||||||
class _Keydefaultdict(collections.defaultdict):
|
class _Keydefaultdict(collections.defaultdict):
|
||||||
"""Unlisted keys help form their own error message.
|
"""Unlisted keys help form their own error message.
|
||||||
|
|
||||||
|
|
@ -121,12 +122,12 @@ ROMM_RGB = 21
|
||||||
|
|
||||||
_factory = lambda x: '{0} (unrecognized)'.format(x)
|
_factory = lambda x: '{0} (unrecognized)'.format(x)
|
||||||
_COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
_COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
||||||
{ CMYK: 'CMYK',
|
{CMYK: 'CMYK',
|
||||||
SRGB: 'sRGB',
|
SRGB: 'sRGB',
|
||||||
GREYSCALE: 'greyscale',
|
GREYSCALE: 'greyscale',
|
||||||
YCC: 'YCC',
|
YCC: 'YCC',
|
||||||
E_SRGB: 'e-sRGB',
|
E_SRGB: 'e-sRGB',
|
||||||
ROMM_RGB: 'ROMM-RGB'} )
|
ROMM_RGB: 'ROMM-RGB'})
|
||||||
|
|
||||||
# enumerated color channel types
|
# enumerated color channel types
|
||||||
COLOR = 0
|
COLOR = 0
|
||||||
|
|
@ -134,11 +135,11 @@ OPACITY = 1
|
||||||
PRE_MULTIPLIED_OPACITY = 2
|
PRE_MULTIPLIED_OPACITY = 2
|
||||||
_UNSPECIFIED = 65535
|
_UNSPECIFIED = 65535
|
||||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||||
_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
_dict = {COLOR: 'color',
|
||||||
{ COLOR: 'color',
|
OPACITY: 'opacity',
|
||||||
OPACITY: 'opacity',
|
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
||||||
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
_UNSPECIFIED: 'unspecified'}
|
||||||
_UNSPECIFIED: 'unspecified'})
|
_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory, _dict)
|
||||||
|
|
||||||
# color channel definitions.
|
# color channel definitions.
|
||||||
RED = 1
|
RED = 1
|
||||||
|
|
@ -153,4 +154,3 @@ _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3},
|
||||||
YCC: {"Y": 1, "Cb": 2, "Cr": 3},
|
YCC: {"Y": 1, "Cb": 2, "Cr": 3},
|
||||||
E_SRGB: {"R": 1, "G": 2, "B": 3},
|
E_SRGB: {"R": 1, "G": 2, "B": 3},
|
||||||
ROMM_RGB: {"R": 1, "G": 2, "B": 3}}
|
ROMM_RGB: {"R": 1, "G": 2, "B": 3}}
|
||||||
|
|
||||||
|
|
|
||||||
119
glymur/jp2box.py
119
glymur/jp2box.py
|
|
@ -29,13 +29,11 @@ import lxml.etree as ET
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from .codestream import Codestream
|
from .codestream import Codestream
|
||||||
from .core import (
|
from .core import (_COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY,
|
||||||
_COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY,
|
SRGB, GREYSCALE, YCC,
|
||||||
SRGB, GREYSCALE, YCC,
|
ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE,
|
||||||
ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE,
|
ANY_ICC_PROFILE, VENDOR_COLOR_METHOD,
|
||||||
ANY_ICC_PROFILE, VENDOR_COLOR_METHOD,
|
_Keydefaultdict)
|
||||||
_Keydefaultdict
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import _uuid_io
|
from . import _uuid_io
|
||||||
|
|
||||||
|
|
@ -52,6 +50,7 @@ _APPROX_DISPLAY = _Keydefaultdict(_factory,
|
||||||
3: 'approximates correct colorspace definition, reasonable quality',
|
3: 'approximates correct colorspace definition, reasonable quality',
|
||||||
4: 'approximates correct colorspace definition, poor quality'})
|
4: 'approximates correct colorspace definition, poor quality'})
|
||||||
|
|
||||||
|
|
||||||
class Jp2kBox(object):
|
class Jp2kBox(object):
|
||||||
"""Superclass for JPEG 2000 boxes.
|
"""Superclass for JPEG 2000 boxes.
|
||||||
|
|
||||||
|
|
@ -109,7 +108,6 @@ class Jp2kBox(object):
|
||||||
msg += '\n' + self._indent(boxstr)
|
msg += '\n' + self._indent(boxstr)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def _indent(self, textstr, indent_level=4):
|
def _indent(self, textstr, indent_level=4):
|
||||||
"""
|
"""
|
||||||
Indent a string.
|
Indent a string.
|
||||||
|
|
@ -135,7 +133,6 @@ class Jp2kBox(object):
|
||||||
lst = [(' ' * indent_level + x) for x in textstr.split('\n')]
|
lst = [(' ' * indent_level + x) for x in textstr.split('\n')]
|
||||||
return '\n'.join(lst)
|
return '\n'.join(lst)
|
||||||
|
|
||||||
|
|
||||||
def _write_superbox(self, fptr, box_id):
|
def _write_superbox(self, fptr, box_id):
|
||||||
"""Write a superbox.
|
"""Write a superbox.
|
||||||
|
|
||||||
|
|
@ -191,13 +188,14 @@ class Jp2kBox(object):
|
||||||
try:
|
try:
|
||||||
box = parser(fptr, start, num_bytes)
|
box = parser(fptr, start, num_bytes)
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
msg = "Encountered an unrecoverable ValueError while parsing a {0} "
|
msg = "Encountered an unrecoverable ValueError while parsing a "
|
||||||
msg += "box at byte offset {1}. The original error message was "
|
msg += "{0} box at byte offset {1}. The original error message "
|
||||||
msg += "\"{2}\""
|
msg += "was \"{2}\""
|
||||||
msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err))
|
msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err))
|
||||||
warnings.warn(msg, UserWarning)
|
warnings.warn(msg, UserWarning)
|
||||||
box = UnknownBox(box_id.decode('utf-8'),
|
box = UnknownBox(box_id.decode('utf-8'),
|
||||||
length=num_bytes, offset=start, longname='Unknown')
|
length=num_bytes,
|
||||||
|
offset=start, longname='Unknown')
|
||||||
|
|
||||||
return box
|
return box
|
||||||
|
|
||||||
|
|
@ -299,6 +297,7 @@ class ColourSpecificationBox(Jp2kBox):
|
||||||
"""
|
"""
|
||||||
longname = 'Colour Specification'
|
longname = 'Colour Specification'
|
||||||
box_id = 'colr'
|
box_id = 'colr'
|
||||||
|
|
||||||
def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0,
|
def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0,
|
||||||
approximation=0, colorspace=None, icc_profile=None,
|
approximation=0, colorspace=None, icc_profile=None,
|
||||||
length=0, offset=-1):
|
length=0, offset=-1):
|
||||||
|
|
@ -337,16 +336,16 @@ class ColourSpecificationBox(Jp2kBox):
|
||||||
|
|
||||||
if self.icc_profile is None:
|
if self.icc_profile is None:
|
||||||
if self.colorspace not in [SRGB, GREYSCALE, YCC]:
|
if self.colorspace not in [SRGB, GREYSCALE, YCC]:
|
||||||
msg = "Colorspace should correspond to one of SRGB, GREYSCALE, "
|
msg = "Colorspace should correspond to one of SRGB, "
|
||||||
msg += "or YCC."
|
msg += "GREYSCALE, or YCC."
|
||||||
self._dispatch_validation_error(msg, writing=True)
|
self._dispatch_validation_error(msg, writing=True)
|
||||||
|
|
||||||
self._validate(writing=True)
|
self._validate(writing=True)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.ColourSpecificationBox("
|
msg = "glymur.jp2box.ColourSpecificationBox("
|
||||||
msg += "method={0}, precedence={1}, approximation={2}, colorspace={3}, "
|
msg += "method={0}, precedence={1}, approximation={2}, "
|
||||||
|
msg += "colorspace={3}, "
|
||||||
msg += "icc_profile={4})"
|
msg += "icc_profile={4})"
|
||||||
msg = msg.format(self.method,
|
msg = msg.format(self.method,
|
||||||
self.precedence,
|
self.precedence,
|
||||||
|
|
@ -357,7 +356,7 @@ class ColourSpecificationBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method])
|
msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method])
|
||||||
|
|
@ -619,10 +618,9 @@ class ChannelDefinitionBox(Jp2kBox):
|
||||||
msg += " 65535 - unspecified"
|
msg += " 65535 - unspecified"
|
||||||
self._dispatch_validation_error(msg, writing=writing)
|
self._dispatch_validation_error(msg, writing=writing)
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
for j in range(len(self.association)):
|
for j in range(len(self.association)):
|
||||||
|
|
@ -842,7 +840,7 @@ class CompositingLayerHeaderBox(Jp2kBox):
|
||||||
List of boxes contained in this superbox.
|
List of boxes contained in this superbox.
|
||||||
"""
|
"""
|
||||||
box_id = 'jplh'
|
box_id = 'jplh'
|
||||||
longname='Compositing Layer Header'
|
longname = 'Compositing Layer Header'
|
||||||
|
|
||||||
def __init__(self, box=None, length=0, offset=-1):
|
def __init__(self, box=None, length=0, offset=-1):
|
||||||
Jp2kBox.__init__(self)
|
Jp2kBox.__init__(self)
|
||||||
|
|
@ -931,7 +929,7 @@ class ComponentMappingBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
for k in range(len(self.component_index)):
|
for k in range(len(self.component_index)):
|
||||||
|
|
@ -1027,7 +1025,9 @@ class ContiguousCodestreamBox(Jp2kBox):
|
||||||
if self._filename is not None:
|
if self._filename is not None:
|
||||||
with open(self._filename, 'rb') as fptr:
|
with open(self._filename, 'rb') as fptr:
|
||||||
fptr.seek(self.main_header_offset)
|
fptr.seek(self.main_header_offset)
|
||||||
main_header = Codestream(fptr, self._length, header_only=True)
|
main_header = Codestream(fptr,
|
||||||
|
self._length,
|
||||||
|
header_only=True)
|
||||||
self._main_header = main_header
|
self._main_header = main_header
|
||||||
return self._main_header
|
return self._main_header
|
||||||
|
|
||||||
|
|
@ -1037,9 +1037,9 @@ class ContiguousCodestreamBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
if _printoptions['codestream'] == False:
|
if _printoptions['codestream'] is False:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n Main header:'
|
msg += '\n Main header:'
|
||||||
|
|
@ -1118,7 +1118,8 @@ class DataReferenceBox(Jp2kBox):
|
||||||
"""Verify that the box obeys the specifications for writing.
|
"""Verify that the box obeys the specifications for writing.
|
||||||
"""
|
"""
|
||||||
if len(self.DR) == 0:
|
if len(self.DR) == 0:
|
||||||
msg = "A data reference box cannot be empty when written to a file."
|
msg = "A data reference box cannot be empty when written to a "
|
||||||
|
msg += "file."
|
||||||
self._dispatch_validation_error(msg, writing=True)
|
self._dispatch_validation_error(msg, writing=True)
|
||||||
self._validate(writing=True)
|
self._validate(writing=True)
|
||||||
|
|
||||||
|
|
@ -1145,7 +1146,7 @@ class DataReferenceBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
for box in self.DR:
|
for box in self.DR:
|
||||||
|
|
@ -1248,7 +1249,7 @@ class FileTypeBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
lst = [msg,
|
lst = [msg,
|
||||||
|
|
@ -1311,7 +1312,7 @@ class FileTypeBox(Jp2kBox):
|
||||||
brand = brand.decode('utf-8')
|
brand = brand.decode('utf-8')
|
||||||
|
|
||||||
# Extract the compatibility list. Each entry has 4 bytes.
|
# Extract the compatibility list. Each entry has 4 bytes.
|
||||||
num_entries = int((length - 16)/ 4)
|
num_entries = int((length - 16) / 4)
|
||||||
compatibility_list = []
|
compatibility_list = []
|
||||||
for j in range(int(num_entries)):
|
for j in range(int(num_entries)):
|
||||||
entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4)
|
entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4)
|
||||||
|
|
@ -1374,7 +1375,7 @@ class FragmentListBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
for j in range(len(self.fragment_offset)):
|
for j in range(len(self.fragment_offset)):
|
||||||
|
|
@ -1458,7 +1459,10 @@ class FragmentTableBox(Jp2kBox):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.FragmentTableBox(box={0})"
|
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
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -1505,7 +1509,6 @@ class FragmentTableBox(Jp2kBox):
|
||||||
self._write_superbox(fptr, b'ftbl')
|
self._write_superbox(fptr, b'ftbl')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FreeBox(Jp2kBox):
|
class FreeBox(Jp2kBox):
|
||||||
"""Container for JPX free box information.
|
"""Container for JPX free box information.
|
||||||
|
|
||||||
|
|
@ -1534,7 +1537,7 @@ class FreeBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
@ -1630,7 +1633,7 @@ class ImageHeaderBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg = "{0}"
|
msg = "{0}"
|
||||||
|
|
@ -1861,7 +1864,7 @@ class JPEG2000SignatureBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
|
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
|
||||||
|
|
@ -1950,7 +1953,7 @@ class PaletteBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
|
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
|
||||||
|
|
@ -2203,7 +2206,7 @@ class ReaderRequirementsBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n Fully Understands Aspect Mask: 0x{0:x}'.format(self.fuam)
|
msg += '\n Fully Understands Aspect Mask: 0x{0:x}'.format(self.fuam)
|
||||||
|
|
@ -2262,7 +2265,8 @@ class ReaderRequirementsBox(Jp2kBox):
|
||||||
standard_flag, standard_mask = data
|
standard_flag, standard_mask = data
|
||||||
|
|
||||||
nflags = len(standard_flag)
|
nflags = len(standard_flag)
|
||||||
vendor_offset = 1 + 2 * mask_length + 2 + (2 + mask_length) * nflags
|
vendor_offset = 1 + 2 * mask_length + 2 \
|
||||||
|
+ (2 + mask_length) * nflags
|
||||||
data = _parse_vendor_features(read_buffer[vendor_offset:],
|
data = _parse_vendor_features(read_buffer[vendor_offset:],
|
||||||
mask_length)
|
mask_length)
|
||||||
vendor_feature, vendor_mask = data
|
vendor_feature, vendor_mask = data
|
||||||
|
|
@ -2348,14 +2352,11 @@ def _parse_standard_flag(read_buffer, mask_length):
|
||||||
# from the buffer read from file.
|
# from the buffer read from file.
|
||||||
mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length]
|
mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length]
|
||||||
|
|
||||||
#read_buffer = fptr.read(2)
|
|
||||||
num_standard_flags, = struct.unpack_from('>H', read_buffer, offset=0)
|
num_standard_flags, = struct.unpack_from('>H', read_buffer, offset=0)
|
||||||
|
|
||||||
# Read in standard flags and standard masks. Each standard flag should
|
# Read in standard flags and standard masks. Each standard flag should
|
||||||
# be two bytes, but the standard mask flag is as long as specified by
|
# be two bytes, but the standard mask flag is as long as specified by
|
||||||
# the mask length.
|
# the mask length.
|
||||||
#read_buffer = fptr.read(num_standard_flags * (2 + mask_length))
|
|
||||||
|
|
||||||
fmt = '>' + ('H' + mask_format) * num_standard_flags
|
fmt = '>' + ('H' + mask_format) * num_standard_flags
|
||||||
data = struct.unpack_from(fmt, read_buffer, offset=2)
|
data = struct.unpack_from(fmt, read_buffer, offset=2)
|
||||||
|
|
||||||
|
|
@ -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
|
# Each vendor feature consists of a 16-byte UUID plus a mask whose
|
||||||
# length is specified by, you guessed it, "mask_length".
|
# length is specified by, you guessed it, "mask_length".
|
||||||
entry_length = 16 + mask_length
|
entry_length = 16 + mask_length
|
||||||
#read_buffer = fptr.read(num_vendor_features * entry_length)
|
|
||||||
vendor_feature = []
|
vendor_feature = []
|
||||||
vendor_mask = []
|
vendor_mask = []
|
||||||
for j in range(num_vendor_features):
|
for j in range(num_vendor_features):
|
||||||
|
|
@ -2494,7 +2494,7 @@ class CaptureResolutionBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n VCR: {0}'.format(self.vertical_resolution)
|
msg += '\n VCR: {0}'.format(self.vertical_resolution)
|
||||||
|
|
@ -2560,7 +2560,7 @@ class DisplayResolutionBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n VDR: {0}'.format(self.vertical_resolution)
|
msg += '\n VDR: {0}'.format(self.vertical_resolution)
|
||||||
|
|
@ -2620,7 +2620,7 @@ class LabelBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n Label: {0}'.format(self.label)
|
msg += '\n Label: {0}'.format(self.label)
|
||||||
|
|
@ -2688,7 +2688,7 @@ class NumberListBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
for j, association in enumerate(self.associations):
|
for j, association in enumerate(self.associations):
|
||||||
|
|
@ -2738,7 +2738,8 @@ class NumberListBox(Jp2kBox):
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a NumberList box to file.
|
"""Write a NumberList box to file.
|
||||||
"""
|
"""
|
||||||
fptr.write(struct.pack('>I4s', len(self.associations) * 4 + 8, b'nlst'))
|
fptr.write(struct.pack('>I4s',
|
||||||
|
len(self.associations) * 4 + 8, b'nlst'))
|
||||||
|
|
||||||
fmt = '>' + 'I' * len(self.associations)
|
fmt = '>' + 'I' * len(self.associations)
|
||||||
write_buffer = struct.pack(fmt, *self.associations)
|
write_buffer = struct.pack(fmt, *self.associations)
|
||||||
|
|
@ -2790,9 +2791,9 @@ class XMLBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
if _printoptions['xml'] == False:
|
if _printoptions['xml'] is False:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n'
|
msg += '\n'
|
||||||
|
|
@ -2911,7 +2912,7 @@ class UUIDListBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
for j, uuid_item in enumerate(self.ulst):
|
for j, uuid_item in enumerate(self.ulst):
|
||||||
|
|
@ -2942,7 +2943,7 @@ class UUIDListBox(Jp2kBox):
|
||||||
|
|
||||||
ulst = []
|
ulst = []
|
||||||
for j in range(num_uuids):
|
for j in range(num_uuids):
|
||||||
uuid_buffer = read_buffer[2 + j * 16 : 2 + (j + 1) * 16]
|
uuid_buffer = read_buffer[2 + j * 16:2 + (j + 1) * 16]
|
||||||
ulst.append(uuid.UUID(bytes=uuid_buffer))
|
ulst.append(uuid.UUID(bytes=uuid_buffer))
|
||||||
|
|
||||||
return cls(ulst, length=length, offset=offset)
|
return cls(ulst, length=length, offset=offset)
|
||||||
|
|
@ -3056,7 +3057,6 @@ class DataEntryURLBox(Jp2kBox):
|
||||||
fptr.write(write_buffer)
|
fptr.write(write_buffer)
|
||||||
fptr.write(url)
|
fptr.write(url)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')"
|
msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')"
|
||||||
msg = msg.format(self.version, self.flag, self.url)
|
msg = msg.format(self.version, self.flag, self.url)
|
||||||
|
|
@ -3064,7 +3064,7 @@ class DataEntryURLBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg += '\n '
|
msg += '\n '
|
||||||
|
|
@ -3216,7 +3216,7 @@ class UUIDBox(Jp2kBox):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
msg = '{0}\n UUID: {1}'.format(msg, self.uuid)
|
msg = '{0}\n UUID: {1}'.format(msg, self.uuid)
|
||||||
|
|
@ -3227,7 +3227,7 @@ class UUIDBox(Jp2kBox):
|
||||||
else:
|
else:
|
||||||
msg += ' (unknown)'
|
msg += ' (unknown)'
|
||||||
|
|
||||||
if (((_printoptions['xml'] == False) and
|
if (((_printoptions['xml'] is False) and
|
||||||
(self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
|
(self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
|
||||||
# If it's an XMP UUID, don't print the XML contents.
|
# If it's an XMP UUID, don't print the XML contents.
|
||||||
return msg
|
return msg
|
||||||
|
|
@ -3312,6 +3312,7 @@ _BOX_WITH_ID = {
|
||||||
|
|
||||||
_parseoptions = {'codestream': True}
|
_parseoptions = {'codestream': True}
|
||||||
|
|
||||||
|
|
||||||
def set_parseoptions(codestream=True):
|
def set_parseoptions(codestream=True):
|
||||||
"""Set parsing options.
|
"""Set parsing options.
|
||||||
|
|
||||||
|
|
@ -3336,6 +3337,7 @@ def set_parseoptions(codestream=True):
|
||||||
"""
|
"""
|
||||||
_parseoptions['codestream'] = codestream
|
_parseoptions['codestream'] = codestream
|
||||||
|
|
||||||
|
|
||||||
def get_parseoptions():
|
def get_parseoptions():
|
||||||
"""Return the current parsing options.
|
"""Return the current parsing options.
|
||||||
|
|
||||||
|
|
@ -3356,6 +3358,7 @@ def get_parseoptions():
|
||||||
|
|
||||||
_printoptions = {'short': False, 'xml': True, 'codestream': True}
|
_printoptions = {'short': False, 'xml': True, 'codestream': True}
|
||||||
|
|
||||||
|
|
||||||
def set_printoptions(**kwargs):
|
def set_printoptions(**kwargs):
|
||||||
"""Set printing options.
|
"""Set printing options.
|
||||||
|
|
||||||
|
|
@ -3365,7 +3368,8 @@ def set_printoptions(**kwargs):
|
||||||
----------
|
----------
|
||||||
short : bool, optional
|
short : bool, optional
|
||||||
When True, only the box ID, offset, and length are displayed. Useful
|
When True, only the box ID, offset, and length are displayed. Useful
|
||||||
for displaying only the basic structure or skeleton of a JPEG 2000 file.
|
for displaying only the basic structure or skeleton of a JPEG 2000
|
||||||
|
file.
|
||||||
xml : bool, optional
|
xml : bool, optional
|
||||||
When False, printing of the XML contents of any XML boxes or UUID XMP
|
When False, printing of the XML contents of any XML boxes or UUID XMP
|
||||||
boxes is suppressed.
|
boxes is suppressed.
|
||||||
|
|
@ -3388,6 +3392,7 @@ def set_printoptions(**kwargs):
|
||||||
raise TypeError('"{0}" not a valid keyword parameter.'.format(key))
|
raise TypeError('"{0}" not a valid keyword parameter.'.format(key))
|
||||||
_printoptions[key] = value
|
_printoptions[key] = value
|
||||||
|
|
||||||
|
|
||||||
def get_printoptions():
|
def get_printoptions():
|
||||||
"""Return the current print options.
|
"""Return the current print options.
|
||||||
|
|
||||||
|
|
@ -3407,5 +3412,3 @@ def get_printoptions():
|
||||||
set_printoptions
|
set_printoptions
|
||||||
"""
|
"""
|
||||||
return _printoptions
|
return _printoptions
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
118
glymur/jp2k.py
118
glymur/jp2k.py
|
|
@ -31,12 +31,12 @@ import numpy as np
|
||||||
|
|
||||||
from .codestream import Codestream
|
from .codestream import Codestream
|
||||||
from . import core, version
|
from . import core, version
|
||||||
from .jp2box import (
|
from .jp2box import (Jp2kBox, JPEG2000SignatureBox, FileTypeBox,
|
||||||
Jp2kBox, JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox,
|
JP2HeaderBox, ColourSpecificationBox,
|
||||||
ColourSpecificationBox, ContiguousCodestreamBox, ImageHeaderBox
|
ContiguousCodestreamBox, ImageHeaderBox)
|
||||||
)
|
|
||||||
from .lib import openjpeg as opj, openjp2 as opj2, c as libc
|
from .lib import openjpeg as opj, openjp2 as opj2, c as libc
|
||||||
|
|
||||||
|
|
||||||
class Jp2k(Jp2kBox):
|
class Jp2k(Jp2kBox):
|
||||||
"""JPEG 2000 file.
|
"""JPEG 2000 file.
|
||||||
|
|
||||||
|
|
@ -138,7 +138,6 @@ class Jp2k(Jp2kBox):
|
||||||
not (X, Y)
|
not (X, Y)
|
||||||
verbose : bool, optional
|
verbose : bool, optional
|
||||||
print informational messages produced by the OpenJPEG library
|
print informational messages produced by the OpenJPEG library
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Jp2kBox.__init__(self)
|
Jp2kBox.__init__(self)
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
@ -176,8 +175,8 @@ class Jp2k(Jp2kBox):
|
||||||
@layer.setter
|
@layer.setter
|
||||||
def layer(self, layer):
|
def layer(self, layer):
|
||||||
if version.openjpeg_version_tuple[0] < 2:
|
if version.openjpeg_version_tuple[0] < 2:
|
||||||
msg = "Layer property not supported unless the version of OpenJPEG "
|
msg = "Layer property not supported unless the version of "
|
||||||
msg += "is 2.0 or higher."
|
msg += "OpenJPEG is 2.0 or higher."
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
self._layer = layer
|
self._layer = layer
|
||||||
|
|
||||||
|
|
@ -361,8 +360,8 @@ class Jp2k(Jp2kBox):
|
||||||
kwargs : dictionary
|
kwargs : dictionary
|
||||||
non-image keyword inputs provided to write method
|
non-image keyword inputs provided to write method
|
||||||
"""
|
"""
|
||||||
if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and
|
if ((('cinema2k' in kwargs or 'cinema4k' in kwargs) and
|
||||||
(len(set(kwargs)) > 1)):
|
(len(set(kwargs)) > 1))):
|
||||||
msg = "Cannot specify cinema2k/cinema4k along with other options."
|
msg = "Cannot specify cinema2k/cinema4k along with other options."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
|
@ -484,9 +483,10 @@ class Jp2k(Jp2kBox):
|
||||||
This method can only be used to create JPEG 2000 images that can fit
|
This method can only be used to create JPEG 2000 images that can fit
|
||||||
in memory.
|
in memory.
|
||||||
"""
|
"""
|
||||||
if re.match("1.[0-4]", version.openjpeg_version) is not None:
|
if re.match("0|1.[0-4]", version.openjpeg_version) is not None:
|
||||||
raise RuntimeError("You must have at least version 1.5 of OpenJPEG "
|
msg = "You must have at least version 1.5 of OpenJPEG "
|
||||||
"in order to write images.")
|
msg += "in order to write images."
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
self._determine_colorspace(**kwargs)
|
self._determine_colorspace(**kwargs)
|
||||||
self._populate_cparams(img_array, **kwargs)
|
self._populate_cparams(img_array, **kwargs)
|
||||||
|
|
@ -518,9 +518,11 @@ class Jp2k(Jp2kBox):
|
||||||
image.contents.x0 = self._cparams.image_offset_x0
|
image.contents.x0 = self._cparams.image_offset_x0
|
||||||
image.contents.y0 = self._cparams.image_offset_y0
|
image.contents.y0 = self._cparams.image_offset_y0
|
||||||
image.contents.x1 = image.contents.x0 \
|
image.contents.x1 = image.contents.x0 \
|
||||||
+ (numcols - 1) * self._cparams.subsampling_dx + 1
|
+ (numcols - 1) * self._cparams.subsampling_dx \
|
||||||
|
+ 1
|
||||||
image.contents.y1 = image.contents.y0 \
|
image.contents.y1 = image.contents.y0 \
|
||||||
+ (numrows - 1) * self._cparams.subsampling_dy + 1
|
+ (numrows - 1) * self._cparams.subsampling_dy \
|
||||||
|
+ 1
|
||||||
|
|
||||||
# Stage the image data to the openjpeg data structure.
|
# Stage the image data to the openjpeg data structure.
|
||||||
for k in range(0, numlayers):
|
for k in range(0, numlayers):
|
||||||
|
|
@ -668,7 +670,6 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
self._colorspace = COLORSPACE_MAP[colorspace.lower()]
|
self._colorspace = COLORSPACE_MAP[colorspace.lower()]
|
||||||
|
|
||||||
|
|
||||||
def _write_openjp2(self, img_array, verbose=False):
|
def _write_openjp2(self, img_array, verbose=False):
|
||||||
"""
|
"""
|
||||||
Write JPEG 2000 file using OpenJPEG 2.x interface.
|
Write JPEG 2000 file using OpenJPEG 2.x interface.
|
||||||
|
|
@ -734,7 +735,8 @@ class Jp2k(Jp2kBox):
|
||||||
if not ((box.box_id == 'xml ') or
|
if not ((box.box_id == 'xml ') or
|
||||||
(box.box_id == 'uuid' and
|
(box.box_id == 'uuid' and
|
||||||
box.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'))):
|
box.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'))):
|
||||||
msg = "Only XML boxes and XMP UUID boxes can currently be appended."
|
msg = "Only XML boxes and XMP UUID boxes can currently be "
|
||||||
|
msg += "appended."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
# Check the last box. If the length field is zero, then rewrite
|
# Check the last box. If the length field is zero, then rewrite
|
||||||
|
|
@ -891,9 +893,9 @@ class Jp2k(Jp2kBox):
|
||||||
Slicing protocol.
|
Slicing protocol.
|
||||||
"""
|
"""
|
||||||
if ((isinstance(index, slice) and
|
if ((isinstance(index, slice) and
|
||||||
(index.start == None and
|
(index.start is None and
|
||||||
index.stop == None and
|
index.stop is None and
|
||||||
index.step == None)) or (index is Ellipsis)):
|
index.step is None)) or (index is Ellipsis)):
|
||||||
# Case of jp2[:] = data, i.e. write the entire image.
|
# Case of jp2[:] = data, i.e. write the entire image.
|
||||||
#
|
#
|
||||||
# Should have a slice object where start = stop = step = None
|
# Should have a slice object where start = stop = step = None
|
||||||
|
|
@ -923,7 +925,9 @@ class Jp2k(Jp2kBox):
|
||||||
return self._read()
|
return self._read()
|
||||||
|
|
||||||
if isinstance(pargs, slice):
|
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[:]
|
# Case of jp2[:]
|
||||||
return self._read()
|
return self._read()
|
||||||
|
|
||||||
|
|
@ -971,7 +975,6 @@ class Jp2k(Jp2kBox):
|
||||||
# Reduce dimensionality in the scalar dimension.
|
# Reduce dimensionality in the scalar dimension.
|
||||||
return np.squeeze(data, axis=idx)
|
return np.squeeze(data, axis=idx)
|
||||||
|
|
||||||
|
|
||||||
# Assuming pargs is a tuple of slices from now on.
|
# Assuming pargs is a tuple of slices from now on.
|
||||||
rows = pargs[0]
|
rows = pargs[0]
|
||||||
cols = pargs[1]
|
cols = pargs[1]
|
||||||
|
|
@ -996,8 +999,7 @@ class Jp2k(Jp2kBox):
|
||||||
raise IndexError(msg)
|
raise IndexError(msg)
|
||||||
rlevel = np.int(np.round(np.log2(step)))
|
rlevel = np.int(np.round(np.log2(step)))
|
||||||
|
|
||||||
area = (
|
area = (0 if rows.start is None else rows.start,
|
||||||
0 if rows.start is None else rows.start,
|
|
||||||
0 if cols.start is None else cols.start,
|
0 if cols.start is None else cols.start,
|
||||||
numrows if rows.stop is None else rows.stop,
|
numrows if rows.stop is None else rows.stop,
|
||||||
numcols if cols.stop is None else cols.stop
|
numcols if cols.stop is None else cols.stop
|
||||||
|
|
@ -1009,7 +1011,6 @@ class Jp2k(Jp2kBox):
|
||||||
# Ok, 3 arguments in pargs.
|
# Ok, 3 arguments in pargs.
|
||||||
return data[:, :, bands]
|
return data[:, :, bands]
|
||||||
|
|
||||||
|
|
||||||
def _read(self, **kwargs):
|
def _read(self, **kwargs):
|
||||||
"""Read a JPEG 2000 image.
|
"""Read a JPEG 2000 image.
|
||||||
|
|
||||||
|
|
@ -1032,33 +1033,33 @@ class Jp2k(Jp2kBox):
|
||||||
def read(self, **kwargs):
|
def read(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
#Read a JPEG 2000 image.
|
# Read a JPEG 2000 image.
|
||||||
#
|
#
|
||||||
#Parameters
|
# Parameters
|
||||||
#----------
|
# ----------
|
||||||
#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 the OpenJPEG version is 1.5 or earlier.
|
# 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
|
||||||
# Specifies decoding image area,
|
# Specifies decoding image area,
|
||||||
# (first_row, first_col, last_row, last_col)
|
# (first_row, first_col, last_row, last_col)
|
||||||
#tile : int, optional
|
# tile : int, optional
|
||||||
# Number of tile to decode.
|
# Number of tile to decode.
|
||||||
#verbose : bool, optional
|
# verbose : bool, optional
|
||||||
# Print informational messages produced by the OpenJPEG library.
|
# Print informational messages produced by the OpenJPEG library.
|
||||||
#
|
#
|
||||||
#Returns
|
# Returns
|
||||||
#-------
|
# -------
|
||||||
#img_array : ndarray
|
# img_array : ndarray
|
||||||
# The image data.
|
# The image data.
|
||||||
#
|
#
|
||||||
#Raises
|
# Raises
|
||||||
#------
|
# ------
|
||||||
#IOError
|
# IOError
|
||||||
# If the image has differing subsample factors.
|
# If the image has differing subsample factors.
|
||||||
|
|
||||||
if 'ignore_pclr_cmap_cdef' in kwargs:
|
if 'ignore_pclr_cmap_cdef' in kwargs:
|
||||||
self.ignore_pclr_cmap_cdef = kwargs['ignore_pclr_cmap_cdef']
|
self.ignore_pclr_cmap_cdef = kwargs['ignore_pclr_cmap_cdef']
|
||||||
|
|
@ -1163,7 +1164,8 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
return data
|
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.
|
"""Read a JPEG 2000 image using libopenjp2.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -1573,6 +1575,7 @@ JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
|
||||||
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
|
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
|
||||||
'uuid']
|
'uuid']
|
||||||
|
|
||||||
|
|
||||||
def _validate_jp2_box_sequence(boxes):
|
def _validate_jp2_box_sequence(boxes):
|
||||||
"""Run through series of tests for JP2 box legality.
|
"""Run through series of tests for JP2 box legality.
|
||||||
|
|
||||||
|
|
@ -1588,12 +1591,13 @@ def _validate_jp2_box_sequence(boxes):
|
||||||
count = _collect_box_count(boxes)
|
count = _collect_box_count(boxes)
|
||||||
for box_id in count.keys():
|
for box_id in count.keys():
|
||||||
if box_id not in JP2_IDS:
|
if box_id not in JP2_IDS:
|
||||||
msg = "The presence of a '{0}' box requires that the file type "
|
msg = "The presence of a '{0}' box requires that the file "
|
||||||
msg += "brand be set to 'jpx '."
|
msg += "type brand be set to 'jpx '."
|
||||||
raise IOError(msg.format(box_id))
|
raise IOError(msg.format(box_id))
|
||||||
|
|
||||||
_validate_jp2_colr(boxes)
|
_validate_jp2_colr(boxes)
|
||||||
|
|
||||||
|
|
||||||
def _validate_jp2_colr(boxes):
|
def _validate_jp2_colr(boxes):
|
||||||
"""
|
"""
|
||||||
Validate JP2 requirements on colour specification boxes.
|
Validate JP2 requirements on colour specification boxes.
|
||||||
|
|
@ -1605,6 +1609,7 @@ def _validate_jp2_colr(boxes):
|
||||||
msg = "A JP2 colr box cannot have a non-zero approximation field."
|
msg = "A JP2 colr box cannot have a non-zero approximation field."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _validate_jpx_box_sequence(boxes):
|
def _validate_jpx_box_sequence(boxes):
|
||||||
"""Run through series of tests for JPX box legality."""
|
"""Run through series of tests for JPX box legality."""
|
||||||
_validate_label(boxes)
|
_validate_label(boxes)
|
||||||
|
|
@ -1613,6 +1618,7 @@ def _validate_jpx_box_sequence(boxes):
|
||||||
_validate_singletons(boxes)
|
_validate_singletons(boxes)
|
||||||
_validate_top_level(boxes)
|
_validate_top_level(boxes)
|
||||||
|
|
||||||
|
|
||||||
def _validate_signature_compatibility(boxes):
|
def _validate_signature_compatibility(boxes):
|
||||||
"""Validate the file signature and compatibility status."""
|
"""Validate the file signature and compatibility status."""
|
||||||
# Check for a bad sequence of boxes.
|
# Check for a bad sequence of boxes.
|
||||||
|
|
@ -1700,6 +1706,8 @@ def _validate_channel_definition(jp2h, colr):
|
||||||
|
|
||||||
|
|
||||||
JP2H_CHILDREN = set(['bpcc', 'cdef', 'cmap', 'ihdr', 'pclr'])
|
JP2H_CHILDREN = set(['bpcc', 'cdef', 'cmap', 'ihdr', 'pclr'])
|
||||||
|
|
||||||
|
|
||||||
def _check_jp2h_child_boxes(boxes, parent_box_name):
|
def _check_jp2h_child_boxes(boxes, parent_box_name):
|
||||||
"""Certain boxes can only reside in the JP2 header."""
|
"""Certain boxes can only reside in the JP2 header."""
|
||||||
box_ids = set([box.box_id for box in boxes])
|
box_ids = set([box.box_id for box in boxes])
|
||||||
|
|
@ -1727,6 +1735,7 @@ def _collect_box_count(boxes):
|
||||||
|
|
||||||
TOP_LEVEL_ONLY_BOXES = set(['dtbl'])
|
TOP_LEVEL_ONLY_BOXES = set(['dtbl'])
|
||||||
|
|
||||||
|
|
||||||
def _check_superbox_for_top_levels(boxes):
|
def _check_superbox_for_top_levels(boxes):
|
||||||
"""Several boxes can only occur at the top level."""
|
"""Several boxes can only occur at the top level."""
|
||||||
# We are only looking at the boxes contained in a superbox, so if any of
|
# We are only looking at the boxes contained in a superbox, so if any of
|
||||||
|
|
@ -1742,6 +1751,7 @@ def _check_superbox_for_top_levels(boxes):
|
||||||
if hasattr(box, 'box'):
|
if hasattr(box, 'box'):
|
||||||
_check_superbox_for_top_levels(box.box)
|
_check_superbox_for_top_levels(box.box)
|
||||||
|
|
||||||
|
|
||||||
def _validate_top_level(boxes):
|
def _validate_top_level(boxes):
|
||||||
"""Several boxes can only occur at the top level."""
|
"""Several boxes can only occur at the top level."""
|
||||||
# Add the counts in the superboxes.
|
# Add the counts in the superboxes.
|
||||||
|
|
@ -1761,6 +1771,7 @@ def _validate_top_level(boxes):
|
||||||
msg += 'a fragment table box as well.'
|
msg += 'a fragment table box as well.'
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _validate_singletons(boxes):
|
def _validate_singletons(boxes):
|
||||||
"""Several boxes can only occur once."""
|
"""Several boxes can only occur once."""
|
||||||
count = _collect_box_count(boxes)
|
count = _collect_box_count(boxes)
|
||||||
|
|
@ -1771,6 +1782,7 @@ def _validate_singletons(boxes):
|
||||||
|
|
||||||
JPX_IDS = ['asoc', 'nlst']
|
JPX_IDS = ['asoc', 'nlst']
|
||||||
|
|
||||||
|
|
||||||
def _validate_jpx_brand(boxes, brand):
|
def _validate_jpx_brand(boxes, brand):
|
||||||
"""
|
"""
|
||||||
If there is a JPX box then the brand must be 'jpx '.
|
If there is a JPX box then the brand must be 'jpx '.
|
||||||
|
|
@ -1785,6 +1797,7 @@ def _validate_jpx_brand(boxes, brand):
|
||||||
# Same set of checks on any child boxes.
|
# Same set of checks on any child boxes.
|
||||||
_validate_jpx_brand(box.box, brand)
|
_validate_jpx_brand(box.box, brand)
|
||||||
|
|
||||||
|
|
||||||
def _validate_jpx_compatibility(boxes, compatibility_list):
|
def _validate_jpx_compatibility(boxes, compatibility_list):
|
||||||
"""
|
"""
|
||||||
If there is a JPX box then the compatibility list must also contain 'jpx '.
|
If there is a JPX box then the compatibility list must also contain 'jpx '.
|
||||||
|
|
@ -1800,6 +1813,7 @@ def _validate_jpx_compatibility(boxes, compatibility_list):
|
||||||
# Same set of checks on any child boxes.
|
# Same set of checks on any child boxes.
|
||||||
_validate_jpx_compatibility(box.box, compatibility_list)
|
_validate_jpx_compatibility(box.box, compatibility_list)
|
||||||
|
|
||||||
|
|
||||||
def _validate_label(boxes):
|
def _validate_label(boxes):
|
||||||
"""
|
"""
|
||||||
Label boxes can only be inside association, codestream headers, or
|
Label boxes can only be inside association, codestream headers, or
|
||||||
|
|
@ -1816,6 +1830,7 @@ def _validate_label(boxes):
|
||||||
# Same set of checks on any child boxes.
|
# Same set of checks on any child boxes.
|
||||||
_validate_label(box.box)
|
_validate_label(box.box)
|
||||||
|
|
||||||
|
|
||||||
def extract_image_cube(image):
|
def extract_image_cube(image):
|
||||||
"""Extract 3D image from openjpeg data structure.
|
"""Extract 3D image from openjpeg data structure.
|
||||||
"""
|
"""
|
||||||
|
|
@ -1871,7 +1886,6 @@ def extract_image_bands(image):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Setup the default callback handlers. See the callback functions subsection
|
# Setup the default callback handlers. See the callback functions subsection
|
||||||
# in the ctypes section of the Python documentation for a solid explanation of
|
# in the ctypes section of the Python documentation for a solid explanation of
|
||||||
# what's going on here.
|
# what's going on here.
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ def glymur_config():
|
||||||
lst.append(load_openjpeg_library(libname))
|
lst.append(load_openjpeg_library(libname))
|
||||||
if all(handle is None for handle in lst):
|
if all(handle is None for handle in lst):
|
||||||
msg = "Neither the openjp2 nor the openjpeg library could be loaded. "
|
msg = "Neither the openjp2 nor the openjpeg library could be loaded. "
|
||||||
raise IOError(msg)
|
warnings.warn(msg)
|
||||||
return tuple(lst)
|
return tuple(lst)
|
||||||
|
|
||||||
def get_configdir():
|
def get_configdir():
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from .config import glymur_config
|
||||||
|
|
||||||
OPENJP2, OPENJPEG = glymur_config()
|
OPENJP2, OPENJPEG = glymur_config()
|
||||||
|
|
||||||
|
|
||||||
def version():
|
def version():
|
||||||
"""Wrapper for opj_version library routine."""
|
"""Wrapper for opj_version library routine."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -50,13 +51,6 @@ JPWL_MAX_NO_TILESPECS = 16
|
||||||
TRUE = 1
|
TRUE = 1
|
||||||
FALSE = 0
|
FALSE = 0
|
||||||
|
|
||||||
#PROFILE = {'none': 0, # No profile
|
|
||||||
# 0: 1, # Profile 0
|
|
||||||
# 1: 2, # Profile 1
|
|
||||||
# 'part2': 0x8000, # At least one extension
|
|
||||||
# 'Cinema2K': 0x0003, # 2K cinema profile
|
|
||||||
# 'Cinema4K': 0x0004, # 4K cinema profile
|
|
||||||
|
|
||||||
# supported color spaces
|
# supported color spaces
|
||||||
CLRSPC_UNKNOWN = -1
|
CLRSPC_UNKNOWN = -1
|
||||||
CLRSPC_UNSPECIFIED = 0
|
CLRSPC_UNSPECIFIED = 0
|
||||||
|
|
@ -548,7 +542,6 @@ class ImageType(ctypes.Structure):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ImageComptParmType(ctypes.Structure):
|
class ImageComptParmType(ctypes.Structure):
|
||||||
"""Component parameters structure used by image_create function.
|
"""Component parameters structure used by image_create function.
|
||||||
|
|
||||||
|
|
@ -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)
|
stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream)
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
|
|
||||||
def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
|
def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
|
||||||
"""Wraps openjp2 library function opj_stream_create_default_vile_stream.
|
"""Wraps openjp2 library function opj_stream_create_default_vile_stream.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,10 @@ class CommonStructType(ctypes.Structure):
|
||||||
("mj2_handle", ctypes.c_void_p)]
|
("mj2_handle", ctypes.c_void_p)]
|
||||||
|
|
||||||
|
|
||||||
STREAM_READ = 0x0001 # The stream was opened for reading.
|
STREAM_READ = 0x0001 # The stream was opened for reading.
|
||||||
STREAM_WRITE = 0x0002 # The stream was opened for writing.
|
STREAM_WRITE = 0x0002 # The stream was opened for writing.
|
||||||
|
|
||||||
|
|
||||||
class CioType(ctypes.Structure):
|
class CioType(ctypes.Structure):
|
||||||
"""Byte input-output stream (CIO)
|
"""Byte input-output stream (CIO)
|
||||||
|
|
||||||
|
|
@ -91,70 +93,57 @@ class CompressionInfoType(CommonStructType):
|
||||||
class PocType(ctypes.Structure):
|
class PocType(ctypes.Structure):
|
||||||
"""Progression order changes."""
|
"""Progression order changes."""
|
||||||
_fields_ = [("resno", ctypes.c_int),
|
_fields_ = [("resno", ctypes.c_int),
|
||||||
# Resolution num start, Component num start, given by POC
|
# Resolution num start, Component num start, given by POC
|
||||||
("compno0", ctypes.c_int),
|
("compno0", ctypes.c_int),
|
||||||
|
|
||||||
# Layer num end,Resolution num end, Component num end, given by POC
|
# Layer num end,Resolution num end, Component num end, given
|
||||||
("layno1", ctypes.c_int),
|
# by POC
|
||||||
("resno1", ctypes.c_int),
|
("layno1", ctypes.c_int),
|
||||||
("compno1", ctypes.c_int),
|
("resno1", ctypes.c_int),
|
||||||
|
("compno1", ctypes.c_int),
|
||||||
|
|
||||||
# Layer num start,Precinct num start, Precinct num end
|
# Layer num start,Precinct num start, Precinct num end
|
||||||
("layno0", ctypes.c_int),
|
("layno0", ctypes.c_int),
|
||||||
("precno0", ctypes.c_int),
|
("precno0", ctypes.c_int),
|
||||||
("precno1", ctypes.c_int),
|
("precno1", ctypes.c_int),
|
||||||
|
|
||||||
# Progression order enum
|
# Progression order enum
|
||||||
# OPJ_PROG_ORDER prg1,prg;
|
# OPJ_PROG_ORDER prg1,prg;
|
||||||
("prg1", ctypes.c_int),
|
("prg1", ctypes.c_int),
|
||||||
("prg", ctypes.c_int),
|
("prg", ctypes.c_int),
|
||||||
|
|
||||||
# Progression order string
|
# Progression order string
|
||||||
# char progorder[5];
|
# char progorder[5];
|
||||||
("progorder", ctypes.c_char * 5),
|
("progorder", ctypes.c_char * 5),
|
||||||
|
|
||||||
# Tile number
|
# Tile number
|
||||||
# int tile;
|
# int tile;
|
||||||
("tile", ctypes.c_int),
|
("tile", ctypes.c_int),
|
||||||
|
|
||||||
# /** Start and end values for Tile width and height*/
|
("tx0", ctypes.c_int),
|
||||||
# int tx0,tx1,ty0,ty1;
|
("tx1", ctypes.c_int),
|
||||||
("tx0", ctypes.c_int),
|
("ty0", ctypes.c_int),
|
||||||
("tx1", ctypes.c_int),
|
("ty1", ctypes.c_int),
|
||||||
("ty0", ctypes.c_int),
|
("layS", ctypes.c_int),
|
||||||
("ty1", ctypes.c_int),
|
("resS", ctypes.c_int),
|
||||||
|
("compS", ctypes.c_int),
|
||||||
# /** Start value, initialised in pi_initialise_encode*/
|
("prcS", ctypes.c_int),
|
||||||
# int layS, resS, compS, prcS;
|
("layE", ctypes.c_int),
|
||||||
("layS", ctypes.c_int),
|
("resE", ctypes.c_int),
|
||||||
("resS", ctypes.c_int),
|
("compE", ctypes.c_int),
|
||||||
("compS", ctypes.c_int),
|
("prcE", ctypes.c_int),
|
||||||
("prcS", ctypes.c_int),
|
("txS", ctypes.c_int),
|
||||||
|
("txE", ctypes.c_int),
|
||||||
# /** End value, initialised in pi_initialise_encode */
|
("tyS", ctypes.c_int),
|
||||||
# int layE, resE, compE, prcE;
|
("tyE", ctypes.c_int),
|
||||||
("layE", ctypes.c_int),
|
("dx", ctypes.c_int),
|
||||||
("resE", ctypes.c_int),
|
("dy", ctypes.c_int),
|
||||||
("compE", ctypes.c_int),
|
("lay_t", ctypes.c_int),
|
||||||
("prcE", ctypes.c_int),
|
("res_t", ctypes.c_int),
|
||||||
|
("comp_t", ctypes.c_int),
|
||||||
# Start and end values of Tile width and height, initialised in
|
("prc_t", ctypes.c_int),
|
||||||
# pi_initialise_encode int txS,txE,tyS,tyE,dx,dy;
|
("tx0_t", ctypes.c_int),
|
||||||
("txS", ctypes.c_int),
|
("ty0_t", ctypes.c_int)]
|
||||||
("txE", ctypes.c_int),
|
|
||||||
("tyS", ctypes.c_int),
|
|
||||||
("tyE", ctypes.c_int),
|
|
||||||
("dx", ctypes.c_int),
|
|
||||||
("dy", ctypes.c_int),
|
|
||||||
|
|
||||||
# Temporary values for Tile parts, initialised in pi_create_encode
|
|
||||||
# int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t;
|
|
||||||
("lay_t", ctypes.c_int),
|
|
||||||
("res_t", ctypes.c_int),
|
|
||||||
("comp_t", ctypes.c_int),
|
|
||||||
("prc_t", ctypes.c_int),
|
|
||||||
("tx0_t", ctypes.c_int),
|
|
||||||
("ty0_t", ctypes.c_int)]
|
|
||||||
|
|
||||||
|
|
||||||
class CompressionParametersType(ctypes.Structure):
|
class CompressionParametersType(ctypes.Structure):
|
||||||
|
|
@ -375,48 +364,47 @@ class DecompressionParametersType(ctypes.Structure):
|
||||||
class ImageComptParmType(ctypes.Structure):
|
class ImageComptParmType(ctypes.Structure):
|
||||||
"""Component parameters structure used by the opj_image_create function.
|
"""Component parameters structure used by the opj_image_create function.
|
||||||
"""
|
"""
|
||||||
_fields_ = [
|
_fields_ = [# XRsiz: horizontal separation of a sample of ith component
|
||||||
# XRsiz: horizontal separation of a sample of ith component with
|
# with respect to the reference grid
|
||||||
# respect to the reference grid
|
("dx", ctypes.c_int),
|
||||||
("dx", ctypes.c_int),
|
|
||||||
|
|
||||||
# YRsiz: vertical separation of a sample of ith component with
|
# YRsiz: vertical separation of a sample of ith component with
|
||||||
# respect to the reference grid */
|
# respect to the reference grid */
|
||||||
("dy", ctypes.c_int),
|
("dy", ctypes.c_int),
|
||||||
|
|
||||||
# data width, height
|
# data width, height
|
||||||
("w", ctypes.c_int),
|
("w", ctypes.c_int),
|
||||||
("h", ctypes.c_int),
|
("h", ctypes.c_int),
|
||||||
|
|
||||||
# x component offset compared to the whole image
|
# x component offset compared to the whole image
|
||||||
# y component offset compared to the whole image
|
# y component offset compared to the whole image
|
||||||
("x0", ctypes.c_int),
|
("x0", ctypes.c_int),
|
||||||
("y0", ctypes.c_int),
|
("y0", ctypes.c_int),
|
||||||
|
|
||||||
# precision
|
# precision
|
||||||
('prec', ctypes.c_int),
|
('prec', ctypes.c_int),
|
||||||
|
|
||||||
# image depth in bits
|
# image depth in bits
|
||||||
('bpp', ctypes.c_int),
|
('bpp', ctypes.c_int),
|
||||||
|
|
||||||
# signed (1) / unsigned (0)
|
# signed (1) / unsigned (0)
|
||||||
('sgnd', ctypes.c_int)]
|
('sgnd', ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
class ImageCompType(ctypes.Structure):
|
class ImageCompType(ctypes.Structure):
|
||||||
"""Defines a single image component. """
|
"""Defines a single image component. """
|
||||||
_fields_ = [("dx", ctypes.c_int),
|
_fields_ = [("dx", ctypes.c_int),
|
||||||
("dy", ctypes.c_int),
|
("dy", ctypes.c_int),
|
||||||
("w", ctypes.c_int),
|
("w", ctypes.c_int),
|
||||||
("h", ctypes.c_int),
|
("h", ctypes.c_int),
|
||||||
("x0", ctypes.c_int),
|
("x0", ctypes.c_int),
|
||||||
("y0", ctypes.c_int),
|
("y0", ctypes.c_int),
|
||||||
("prec", ctypes.c_int),
|
("prec", ctypes.c_int),
|
||||||
("bpp", ctypes.c_int),
|
("bpp", ctypes.c_int),
|
||||||
("sgnd", ctypes.c_int),
|
("sgnd", ctypes.c_int),
|
||||||
("resno_decoded", ctypes.c_int),
|
("resno_decoded", ctypes.c_int),
|
||||||
("factor", ctypes.c_int),
|
("factor", ctypes.c_int),
|
||||||
("data", ctypes.POINTER(ctypes.c_int))]
|
("data", ctypes.POINTER(ctypes.c_int))]
|
||||||
|
|
||||||
|
|
||||||
class ImageType(ctypes.Structure):
|
class ImageType(ctypes.Structure):
|
||||||
|
|
@ -468,6 +456,7 @@ def cio_tell(cio):
|
||||||
pos = OPENJPEG.cio_tell(cio)
|
pos = OPENJPEG.cio_tell(cio)
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
|
|
||||||
def create_compress(fmt):
|
def create_compress(fmt):
|
||||||
"""Wrapper for openjpeg library function opj_create_compress.
|
"""Wrapper for openjpeg library function opj_create_compress.
|
||||||
|
|
||||||
|
|
@ -585,9 +574,8 @@ def image_cmptparm_t_from_np(np_image):
|
||||||
def image_create(cmptparms, cspace):
|
def image_create(cmptparms, cspace):
|
||||||
"""Wrapper for openjpeg library function opj_image_create.
|
"""Wrapper for openjpeg library function opj_image_create.
|
||||||
"""
|
"""
|
||||||
OPENJPEG.opj_image_create.argtypes = [ctypes.c_int,
|
lst = [ctypes.c_int, ctypes.POINTER(ImageComptParmType), ctypes.c_int]
|
||||||
ctypes.POINTER(ImageComptParmType),
|
OPENJPEG.opj_image_create.argtypes = lst
|
||||||
ctypes.c_int]
|
|
||||||
OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType)
|
OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType)
|
||||||
|
|
||||||
image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace)
|
image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import glymur
|
||||||
from . import fixtures
|
from . import fixtures
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here")
|
@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here")
|
||||||
@unittest.skipIf(re.match('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")
|
"Requires openjpeg 2.1.0 or higher")
|
||||||
class TestPrintingOpenjp2(unittest.TestCase):
|
class TestPrintingOpenjp2(unittest.TestCase):
|
||||||
"""Tests for verifying how printing works on openjp2 library structures."""
|
"""Tests for verifying how printing works on openjp2 library structures."""
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,14 @@ import six
|
||||||
|
|
||||||
import glymur
|
import glymur
|
||||||
|
|
||||||
|
# If openjpeg is not installed, many tests cannot be run.
|
||||||
|
if glymur.version.openjpeg_version == '0.0.0':
|
||||||
|
OPENJPEG_NOT_AVAILABLE = True
|
||||||
|
OPENJPEG_NOT_AVAILABLE_MSG = 'OpenJPEG library not installed'
|
||||||
|
else:
|
||||||
|
OPENJPEG_NOT_AVAILABLE = False
|
||||||
|
OPENJPEG_NOT_AVAILABLE_MSG = None
|
||||||
|
|
||||||
# Some versions of "six" on python3 cause problems when verifying warnings.
|
# Some versions of "six" on python3 cause problems when verifying warnings.
|
||||||
# Only use when the version is 1.7 or higher.
|
# Only use when the version is 1.7 or higher.
|
||||||
# And moreover, we only test using the 3.x infrastructure, never on 2.x.
|
# And moreover, we only test using the 3.x infrastructure, never on 2.x.
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ class TestCallbacks(unittest.TestCase):
|
||||||
expected = '[INFO] tile number 1 / 1'
|
expected = '[INFO] tile number 1 / 1'
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
@unittest.skipIf(glymur.version.openjpeg_version[0] == '0',
|
||||||
|
"Missing openjpeg/openjp2 library.")
|
||||||
def test_info_callbacks_on_read(self):
|
def test_info_callbacks_on_read(self):
|
||||||
"""stdio output when info callback handler is enabled"""
|
"""stdio output when info callback handler is enabled"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ class TestDataEntryURL(unittest.TestCase):
|
||||||
self.assertEqual(url + chr(0), read_url)
|
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,
|
glymur.version.openjpeg_version) is not None,
|
||||||
"Not supported until 2.1")
|
"Not supported until 2.1")
|
||||||
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
|
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ 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
|
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
|
||||||
|
from .fixtures import OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG
|
||||||
|
|
||||||
if HAS_PYTHON_XMP_TOOLKIT:
|
if HAS_PYTHON_XMP_TOOLKIT:
|
||||||
import libxmp
|
import libxmp
|
||||||
|
|
@ -76,6 +77,7 @@ class SliceProtocolBase(unittest.TestCase):
|
||||||
self.j2k_data_r1 = self.j2k[::2, ::2]
|
self.j2k_data_r1 = self.j2k[::2, ::2]
|
||||||
self.j2k_data_r5 = self.j2k[::32, ::32]
|
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,
|
@unittest.skipIf(re.match("1.5|2", glymur.version.openjpeg_version) is None,
|
||||||
"Must have openjpeg 1.5 or higher to run")
|
"Must have openjpeg 1.5 or higher to run")
|
||||||
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
|
@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, :]
|
j[:25, :45, :] = self.j2k_data[:25, :25, :]
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
class TestSliceProtocolRead(SliceProtocolBase):
|
class TestSliceProtocolRead(SliceProtocolBase):
|
||||||
|
|
||||||
def test_resolution_strides_cannot_differ(self):
|
def test_resolution_strides_cannot_differ(self):
|
||||||
|
|
@ -251,6 +254,7 @@ class TestJp2k(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
|
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
|
||||||
def test_warn_if_using_read_method(self):
|
def test_warn_if_using_read_method(self):
|
||||||
"""Should warn if deprecated read method is called"""
|
"""Should warn if deprecated read method is called"""
|
||||||
|
|
@ -313,6 +317,7 @@ class TestJp2k(unittest.TestCase):
|
||||||
actdata = j2[:]
|
actdata = j2[:]
|
||||||
self.assertTrue(fixtures.mse(actdata[0], expdata[0]) < 0.38)
|
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,
|
@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
|
||||||
"Not supported with OpenJPEG {0}".format(openjpeg_version))
|
"Not supported with OpenJPEG {0}".format(openjpeg_version))
|
||||||
@unittest.skipIf(re.match('1.5.(1|2)', openjpeg_version) is not None,
|
@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(newjp2.filename, self.j2kfile)
|
||||||
self.assertEqual(len(newjp2.box), 0)
|
self.assertEqual(len(newjp2.box), 0)
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
|
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
|
||||||
def test_rlevel_max_backwards_compatibility(self):
|
def test_rlevel_max_backwards_compatibility(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -367,6 +373,7 @@ class TestJp2k(unittest.TestCase):
|
||||||
np.testing.assert_array_equal(thumbnail1, thumbnail2)
|
np.testing.assert_array_equal(thumbnail1, thumbnail2)
|
||||||
self.assertEqual(thumbnail1.shape, (25, 15, 3))
|
self.assertEqual(thumbnail1.shape, (25, 15, 3))
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
def test_rlevel_too_high(self):
|
def test_rlevel_too_high(self):
|
||||||
"""Should error out appropriately if reduce level too high"""
|
"""Should error out appropriately if reduce level too high"""
|
||||||
j = Jp2k(self.jp2file)
|
j = Jp2k(self.jp2file)
|
||||||
|
|
@ -518,12 +525,14 @@ class TestJp2k(unittest.TestCase):
|
||||||
self.assertEqual(new_jp2.box[j].length,
|
self.assertEqual(new_jp2.box[j].length,
|
||||||
baseline_jp2.box[j].length)
|
baseline_jp2.box[j].length)
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
def test_basic_jp2(self):
|
def test_basic_jp2(self):
|
||||||
"""Just a very basic test that reading a JP2 file does not error out.
|
"""Just a very basic test that reading a JP2 file does not error out.
|
||||||
"""
|
"""
|
||||||
j2k = Jp2k(self.jp2file)
|
j2k = Jp2k(self.jp2file)
|
||||||
j2k[::2, ::2]
|
j2k[::2, ::2]
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
def test_basic_j2k(self):
|
def test_basic_j2k(self):
|
||||||
"""This test is only useful when openjp2 is not available
|
"""This test is only useful when openjp2 is not available
|
||||||
and OPJ_DATA_ROOT is not set. We need at least one
|
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')
|
creator_tool = xmp.get_property(libxmp.consts.XMP_NS_XMP, 'CreatorTool')
|
||||||
self.assertEqual(creator_tool, 'Google')
|
self.assertEqual(creator_tool, 'Google')
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
|
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
|
||||||
glymur.version.openjpeg_version) is not None,
|
glymur.version.openjpeg_version) is not None,
|
||||||
"Not supported until 2.0.1")
|
"Not supported until 2.0.1")
|
||||||
|
|
@ -664,6 +674,7 @@ class TestJp2k(unittest.TestCase):
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
glymur.Jp2k(self.jp2file).read_bands()
|
glymur.Jp2k(self.jp2file).read_bands()
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
|
@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
|
||||||
"Not supported with OpenJPEG {0}".format(openjpeg_version))
|
"Not supported with OpenJPEG {0}".format(openjpeg_version))
|
||||||
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
|
@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(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
class Test_2p0_official(unittest.TestCase):
|
class Test_2p0_official(unittest.TestCase):
|
||||||
"""Tests specific to v2.0.0"""
|
"""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 ')
|
self.assertEqual(jasoc.box[3].box[1].box_id, 'xml ')
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
|
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
|
||||||
glymur.version.openjpeg_version) is not None,
|
glymur.version.openjpeg_version) is not None,
|
||||||
"Not to be run until unless 2.0.1 or higher is present")
|
"Not to be run until unless 2.0.1 or higher is present")
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,12 @@ from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox
|
||||||
from .fixtures import (
|
from .fixtures import (
|
||||||
OPJ_DATA_ROOT, MetadataBase,
|
OPJ_DATA_ROOT, MetadataBase,
|
||||||
WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
|
WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
|
||||||
mse, peak_tolerance, read_pgx, opj_data_file
|
mse, peak_tolerance, read_pgx, opj_data_file,
|
||||||
|
OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
class TestSuite(unittest.TestCase):
|
class TestSuite(unittest.TestCase):
|
||||||
|
|
@ -482,7 +484,7 @@ class TestSuiteWarns(MetadataBase):
|
||||||
|
|
||||||
@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] != 2,
|
||||||
"Feature not supported in glymur until openjpeg 2.0")
|
"Feature not supported in glymur until openjpeg 2.0")
|
||||||
class TestSuiteBands(unittest.TestCase):
|
class TestSuiteBands(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
@ -577,7 +579,7 @@ class TestSuiteBands(unittest.TestCase):
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
|
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
|
||||||
"Tests not passing until 2.0")
|
"Tests not passing until 2.0")
|
||||||
class TestSuite2point0(unittest.TestCase):
|
class TestSuite2point0(unittest.TestCase):
|
||||||
"""Runs tests introduced in version 2.0 or that pass only in 2.0"""
|
"""Runs tests introduced in version 2.0 or that pass only in 2.0"""
|
||||||
|
|
@ -641,7 +643,7 @@ class TestSuite2point0(unittest.TestCase):
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
|
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
|
||||||
glymur.version.openjpeg_version) is not None,
|
glymur.version.openjpeg_version) is not None,
|
||||||
"Only supported in 2.0.1 or higher")
|
"Only supported in 2.0.1 or higher")
|
||||||
class TestSuite2point1(unittest.TestCase):
|
class TestSuite2point1(unittest.TestCase):
|
||||||
|
|
@ -798,7 +800,7 @@ class TestSuite2point1(unittest.TestCase):
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
|
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
|
||||||
glymur.version.openjpeg_version) is not None,
|
glymur.version.openjpeg_version) is not None,
|
||||||
"Only supported in 2.0.1 or higher")
|
"Only supported in 2.0.1 or higher")
|
||||||
class TestReadArea(unittest.TestCase):
|
class TestReadArea(unittest.TestCase):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue