Merge branch 'issue130' of https://github.com/quintusdias/glymur into issue130
Conflicts: glymur/jp2box.py
This commit is contained in:
commit
6ee48e3f91
28 changed files with 1975 additions and 1075 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
*.pyc
|
||||||
|
|
@ -11,8 +11,8 @@ before_install:
|
||||||
|
|
||||||
# command to install dependencies
|
# command to install dependencies
|
||||||
install:
|
install:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors lxml contextlib2 mock; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install lxml contextlib2 mock; fi
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install --use-mirrors lxml numpy; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install lxml numpy; fi
|
||||||
|
|
||||||
# command to run tests
|
# command to run tests
|
||||||
script:
|
script:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
Feb 09, 2014 - Changed constructor for ChannelDefinition box. Removed support
|
Mar 06, 2014 - Added Cinema2K, Cinema4K write support.
|
||||||
|
Changed constructor for ChannelDefinition box. Removed support
|
||||||
for Python 2.6. Added write support for JP2 UUID, DataEntryURL,
|
for Python 2.6. Added write support for JP2 UUID, DataEntryURL,
|
||||||
Palette and Component Mapping boxes, JPX Association, NumberList
|
Palette and Component Mapping boxes, JPX Association, NumberList
|
||||||
and DataReference boxes. Added read support for JPX free,
|
and DataReference boxes. Added read support for JPX free,
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,12 @@ ChangeLog
|
||||||
0.6.0 (pending)
|
0.6.0 (pending)
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
* Added Cinema2K, Cinema4K write support.
|
||||||
* Added lxml requirement.
|
* Added lxml requirement.
|
||||||
* added set_printoptions, get_printoptions function
|
* added set_printoptions, get_printoptions function
|
||||||
* dropped support for Python 2.6, added support for Python 3.4
|
* dropped support for Python 2.6, added support for Python 3.4
|
||||||
* dropped windows support
|
* dropped support for OpenJPEG versions 1.3 and 1.4
|
||||||
|
* dropped windows support (it might work, it might not, I don't much care)
|
||||||
* added write support for JP2 UUID, dataEntryURL, palette, and component mapping boxes
|
* added write support for JP2 UUID, dataEntryURL, palette, and component mapping boxes
|
||||||
* added read/write support for JPX free, number list, and data reference boxes
|
* added read/write support for JPX free, number list, and data reference boxes
|
||||||
* Added read support for JPX fragment list and fragment table boxes
|
* Added read support for JPX fragment list and fragment table boxes
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ both read and write JPEG 2000 files, but you may wish to install version 2.0
|
||||||
or the 2.0+ version from OpenJPEG's development trunk for better performance.
|
or the 2.0+ version from OpenJPEG's development trunk for better performance.
|
||||||
If you do that, you should compile it as a shared library (named *openjp2*
|
If you do that, you should compile it as a shared library (named *openjp2*
|
||||||
instead of *openjpeg*) from the developmental source that you can retrieve
|
instead of *openjpeg*) from the developmental source that you can retrieve
|
||||||
via subversion. As of this time of writing, svn revision r2354 works.
|
via subversion. As of this time of writing, svn revision r2691 works.
|
||||||
You should also download the test data for the purpose of configuring
|
You should also download the test data for the purpose of configuring
|
||||||
and running OpenJPEG's test suite, check their instructions for all this.
|
and running OpenJPEG's test suite, check their instructions for all this.
|
||||||
You should set the **OPJ_DATA_ROOT** environment variable for the purpose
|
You should set the **OPJ_DATA_ROOT** environment variable for the purpose
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,7 @@ XMP UUIDs. There is some very limited support for reading JPX metadata.
|
||||||
Glymur 0.6 works on Python versions 2.7, 3.3 and 3.4. If you have Python 2.6,
|
Glymur 0.6 works on Python versions 2.7, 3.3 and 3.4. If you have Python 2.6,
|
||||||
you should use the 0.5 series of Glymur.
|
you should use the 0.5 series of Glymur.
|
||||||
|
|
||||||
OpenJPEG Installation
|
For more information about OpenJPEG, please consult http://www.openjpeg.org.
|
||||||
=====================
|
|
||||||
Glymur will read JPEG 2000 images with versions 1.3, 1.4, 1.5, 2.0,
|
|
||||||
and the trunk/development version of OpenJPEG. Writing images is
|
|
||||||
only supported with the 1.5 or better, however, and the trunk/development
|
|
||||||
version of OpenJPEG is strongly recommended. For more information about
|
|
||||||
OpenJPEG, please consult http://www.openjpeg.org.
|
|
||||||
|
|
||||||
If you use MacPorts or if you have a sufficiently recent version of
|
|
||||||
Linux, your package manager should already provide you with a version of
|
|
||||||
OpenJPEG 1.X which glymur can already use.
|
|
||||||
|
|
||||||
Glymur Installation
|
Glymur Installation
|
||||||
===================
|
===================
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ def tiff_header(read_buffer):
|
||||||
endian = '>'
|
endian = '>'
|
||||||
else:
|
else:
|
||||||
msg = "Bad byte order indication: {0}".format(read_buffer[6:8])
|
msg = "Bad byte order indication: {0}".format(read_buffer[6:8])
|
||||||
raise RuntimeError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
_, offset = struct.unpack(endian + 'HI', read_buffer[8:14])
|
_, offset = struct.unpack(endian + 'HI', read_buffer[8:14])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,36 @@ import numpy as np
|
||||||
from .core import LRCP, RLCP, RPCL, PCRL, CPRL
|
from .core import LRCP, RLCP, RPCL, PCRL, CPRL
|
||||||
from .core import WAVELET_XFORM_9X7_IRREVERSIBLE
|
from .core import WAVELET_XFORM_9X7_IRREVERSIBLE
|
||||||
from .core import WAVELET_XFORM_5X3_REVERSIBLE
|
from .core import WAVELET_XFORM_5X3_REVERSIBLE
|
||||||
from .core import _CAPABILITIES_DISPLAY
|
from .core import _Keydefaultdict
|
||||||
from .lib import openjp2 as opj2
|
from .lib import openjp2 as opj2
|
||||||
|
|
||||||
_PROGRESSION_ORDER_DISPLAY = {
|
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||||
LRCP: 'LRCP',
|
_PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory,
|
||||||
RLCP: 'RLCP',
|
{ LRCP: 'LRCP',
|
||||||
RPCL: 'RPCL',
|
RLCP: 'RLCP',
|
||||||
PCRL: 'PCRL',
|
RPCL: 'RPCL',
|
||||||
CPRL: 'CPRL'}
|
PCRL: 'PCRL',
|
||||||
|
CPRL: 'CPRL'})
|
||||||
|
|
||||||
_WAVELET_TRANSFORM_DISPLAY = {
|
_WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory,
|
||||||
WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible',
|
{ WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible',
|
||||||
WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'}
|
WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'})
|
||||||
|
|
||||||
|
_NO_PROFILE = 0
|
||||||
|
_PROFILE_0 = 1
|
||||||
|
_PROFILE_1 = 2
|
||||||
|
_PROFILE_3 = 3
|
||||||
|
_PROFILE_4 = 4
|
||||||
|
|
||||||
|
_KNOWN_PROFILES = [_NO_PROFILE, _PROFILE_0, _PROFILE_1, _PROFILE_3, _PROFILE_4]
|
||||||
|
|
||||||
|
# How to display the codestream profile.
|
||||||
|
_CAPABILITIES_DISPLAY = _Keydefaultdict(_factory,
|
||||||
|
{ _NO_PROFILE: 'no profile',
|
||||||
|
_PROFILE_0: '0',
|
||||||
|
_PROFILE_1: '1',
|
||||||
|
_PROFILE_3: 'Cinema 2K',
|
||||||
|
_PROFILE_4: 'Cinema 4K'} )
|
||||||
|
|
||||||
# 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.
|
||||||
|
|
@ -371,6 +388,14 @@ class Codestream(object):
|
||||||
numbytes = offset + 2 + length - fptr.tell()
|
numbytes = offset + 2 + length - fptr.tell()
|
||||||
spcod = fptr.read(numbytes)
|
spcod = fptr.read(numbytes)
|
||||||
spcod = np.frombuffer(spcod, dtype=np.uint8)
|
spcod = np.frombuffer(spcod, dtype=np.uint8)
|
||||||
|
if spcod[0] not in [LRCP, RLCP, RPCL, PCRL, CPRL]:
|
||||||
|
msg = "Invalid progression order in COD segment: {0}."
|
||||||
|
warnings.warn(msg.format(spcod[0]))
|
||||||
|
|
||||||
|
if spcod[8] not in [WAVELET_XFORM_9X7_IRREVERSIBLE,
|
||||||
|
WAVELET_XFORM_5X3_REVERSIBLE]:
|
||||||
|
msg = "Invalid wavelet transform in COD segment: {0}."
|
||||||
|
warnings.warn(msg.format(spcod[8]))
|
||||||
|
|
||||||
sop = (scod & 2) > 0
|
sop = (scod & 2) > 0
|
||||||
eph = (scod & 4) > 0
|
eph = (scod & 4) > 0
|
||||||
|
|
@ -645,10 +670,13 @@ class Codestream(object):
|
||||||
read_buffer = fptr.read(2)
|
read_buffer = fptr.read(2)
|
||||||
length, = struct.unpack('>H', read_buffer)
|
length, = struct.unpack('>H', read_buffer)
|
||||||
|
|
||||||
xy_buffer = fptr.read(36)
|
read_buffer = fptr.read(length - 2)
|
||||||
data = struct.unpack('>HIIIIIIIIH', xy_buffer)
|
data = struct.unpack_from('>HIIIIIIIIH', read_buffer)
|
||||||
|
|
||||||
rsiz = data[0]
|
rsiz = data[0]
|
||||||
|
if rsiz not in _KNOWN_PROFILES:
|
||||||
|
warnings.warn("Invalid profile: (Rsiz={0}).".format(rsiz))
|
||||||
|
|
||||||
xysiz = (data[1], data[2])
|
xysiz = (data[1], data[2])
|
||||||
xyosiz = (data[3], data[4])
|
xyosiz = (data[3], data[4])
|
||||||
xytsiz = (data[5], data[6])
|
xytsiz = (data[5], data[6])
|
||||||
|
|
@ -657,9 +685,8 @@ class Codestream(object):
|
||||||
# Csiz is the number of components
|
# Csiz is the number of components
|
||||||
Csiz = data[9]
|
Csiz = data[9]
|
||||||
|
|
||||||
component_buffer = fptr.read(Csiz * 3)
|
data = struct.unpack_from('>' + 'B' * (length - 36 - 2),
|
||||||
data = struct.unpack('>' + 'B' * len(component_buffer),
|
read_buffer, offset=36)
|
||||||
component_buffer)
|
|
||||||
|
|
||||||
bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3])
|
bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3])
|
||||||
signed = tuple(((x & 0x80) > 0) for x in data[0::3])
|
signed = tuple(((x & 0x80) > 0) for x in data[0::3])
|
||||||
|
|
@ -673,6 +700,18 @@ class Codestream(object):
|
||||||
msg = msg.format(j, subsampling[0], subsampling[1])
|
msg = msg.format(j, subsampling[0], subsampling[1])
|
||||||
warnings.warn(msg)
|
warnings.warn(msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
num_tiles_x = (xysiz[0] - xyosiz[0]) / (xytsiz[0] - xytosiz[0])
|
||||||
|
num_tiles_y = (xysiz[1] - xyosiz[1]) / (xytsiz[1] - xytosiz[1])
|
||||||
|
except ZeroDivisionError as err:
|
||||||
|
warnings.warn("Invalid tile dimensions.")
|
||||||
|
else:
|
||||||
|
numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y)
|
||||||
|
if numtiles > 65535:
|
||||||
|
msg = "Invalid number of tiles ({0}).".format(numtiles)
|
||||||
|
warnings.warn(msg)
|
||||||
|
|
||||||
|
|
||||||
kwargs = {'rsiz': rsiz,
|
kwargs = {'rsiz': rsiz,
|
||||||
'xysiz': xysiz,
|
'xysiz': xysiz,
|
||||||
'xyosiz': xyosiz,
|
'xyosiz': xyosiz,
|
||||||
|
|
@ -1514,14 +1553,6 @@ class SIZsegment(Segment):
|
||||||
lst.append(bitdepth - 1)
|
lst.append(bitdepth - 1)
|
||||||
self.ssiz = tuple(lst)
|
self.ssiz = tuple(lst)
|
||||||
|
|
||||||
num_tiles_x = (self.xsiz - self.xosiz) / (self.xtsiz - self.xtosiz)
|
|
||||||
num_tiles_y = (self.ysiz - self.yosiz) / (self.ytsiz - self.ytosiz)
|
|
||||||
numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y)
|
|
||||||
if numtiles > 65535:
|
|
||||||
msg = "Invalid number of tiles ({0}).".format(numtiles)
|
|
||||||
warnings.warn(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, "
|
msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, "
|
||||||
msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, "
|
msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, "
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,22 @@
|
||||||
"""Core definitions to be shared amongst the modules.
|
"""Core definitions to be shared amongst the modules.
|
||||||
"""
|
"""
|
||||||
|
import collections
|
||||||
import copy
|
import copy
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
|
class _Keydefaultdict(collections.defaultdict):
|
||||||
|
"""Unlisted keys help form their own error message.
|
||||||
|
|
||||||
|
Normally defaultdict uses a factory function with no input arguments, but
|
||||||
|
that's not quite the behavior we want.
|
||||||
|
"""
|
||||||
|
def __missing__(self, key):
|
||||||
|
if self.default_factory is None:
|
||||||
|
raise KeyError(key)
|
||||||
|
else:
|
||||||
|
ret = self[key] = self.default_factory(key)
|
||||||
|
return ret
|
||||||
|
|
||||||
# Progression order
|
# Progression order
|
||||||
LRCP = 0
|
LRCP = 0
|
||||||
RLCP = 1
|
RLCP = 1
|
||||||
|
|
@ -10,6 +24,26 @@ RPCL = 2
|
||||||
PCRL = 3
|
PCRL = 3
|
||||||
CPRL = 4
|
CPRL = 4
|
||||||
|
|
||||||
|
STD = 0
|
||||||
|
CINEMA2K = 3
|
||||||
|
CINEMA4K = 4
|
||||||
|
|
||||||
|
RSIZ = {
|
||||||
|
'STD': STD,
|
||||||
|
'CINEMA2K': CINEMA2K,
|
||||||
|
'CINEMA4K': CINEMA4K}
|
||||||
|
|
||||||
|
OFF = 0
|
||||||
|
CINEMA2K_24 = 1
|
||||||
|
CINEMA2K_48 = 2
|
||||||
|
CINEMA4K_24 = 3
|
||||||
|
|
||||||
|
CINEMA_MODE = {
|
||||||
|
'off': OFF,
|
||||||
|
'cinema2k_24': CINEMA2K_24,
|
||||||
|
'cinema2k_48': CINEMA2K_48,
|
||||||
|
'cinema4k_24': CINEMA4K_24, }
|
||||||
|
|
||||||
PROGRESSION_ORDER = {
|
PROGRESSION_ORDER = {
|
||||||
'LRCP': LRCP,
|
'LRCP': LRCP,
|
||||||
'RLCP': RLCP,
|
'RLCP': RLCP,
|
||||||
|
|
@ -37,24 +71,26 @@ YCC = 18
|
||||||
E_SRGB = 20
|
E_SRGB = 20
|
||||||
ROMM_RGB = 21
|
ROMM_RGB = 21
|
||||||
|
|
||||||
_COLORSPACE_MAP_DISPLAY = {
|
_factory = lambda x: '{0} (unrecognized)'.format(x)
|
||||||
CMYK: 'CMYK',
|
_COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
||||||
SRGB: 'sRGB',
|
{ CMYK: 'CMYK',
|
||||||
GREYSCALE: 'greyscale',
|
SRGB: 'sRGB',
|
||||||
YCC: 'YCC',
|
GREYSCALE: 'greyscale',
|
||||||
E_SRGB: 'e-sRGB',
|
YCC: 'YCC',
|
||||||
ROMM_RGB: 'ROMM-RGB'}
|
E_SRGB: 'e-sRGB',
|
||||||
|
ROMM_RGB: 'ROMM-RGB'} )
|
||||||
|
|
||||||
# enumerated color channel types
|
# enumerated color channel types
|
||||||
COLOR = 0
|
COLOR = 0
|
||||||
OPACITY = 1
|
OPACITY = 1
|
||||||
PRE_MULTIPLIED_OPACITY = 2
|
PRE_MULTIPLIED_OPACITY = 2
|
||||||
_UNSPECIFIED = 65535
|
_UNSPECIFIED = 65535
|
||||||
_COLOR_TYPE_MAP_DISPLAY = {
|
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||||
COLOR: 'color',
|
_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
||||||
OPACITY: 'opacity',
|
{ COLOR: 'color',
|
||||||
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
OPACITY: 'opacity',
|
||||||
_UNSPECIFIED: 'unspecified'}
|
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
||||||
|
_UNSPECIFIED: 'unspecified'})
|
||||||
|
|
||||||
# color channel definitions.
|
# color channel definitions.
|
||||||
RED = 1
|
RED = 1
|
||||||
|
|
@ -70,9 +106,3 @@ _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 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}}
|
||||||
|
|
||||||
# How to display the codestream profile.
|
|
||||||
_CAPABILITIES_DISPLAY = {
|
|
||||||
0: '2',
|
|
||||||
1: '0',
|
|
||||||
2: '1',
|
|
||||||
3: '3'}
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -34,13 +34,13 @@ def goodstuff():
|
||||||
|
|
||||||
|
|
||||||
def jpxfile():
|
def jpxfile():
|
||||||
"""Shortcut for specifying path to 12-v6.4.jpx.
|
"""Shortcut for specifying path to heliov.jpx.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
file : str
|
file : str
|
||||||
Platform-independent path to 12-v6.4.jpx
|
Platform-independent path to 12-v6.4.jpx
|
||||||
"""
|
"""
|
||||||
filename = pkg_resources.resource_filename(__name__, "12-v6.4.jpx")
|
filename = pkg_resources.resource_filename(__name__, "heliov.jpx")
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
|
||||||
BIN
glymur/data/heliov.jpx
Normal file
BIN
glymur/data/heliov.jpx
Normal file
Binary file not shown.
712
glymur/jp2box.py
712
glymur/jp2box.py
File diff suppressed because it is too large
Load diff
294
glymur/jp2k.py
294
glymur/jp2k.py
|
|
@ -20,6 +20,7 @@ from collections import Counter
|
||||||
import ctypes
|
import ctypes
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import struct
|
import struct
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
import warnings
|
import warnings
|
||||||
|
|
@ -28,7 +29,7 @@ import numpy as np
|
||||||
|
|
||||||
from .codestream import Codestream
|
from .codestream import Codestream
|
||||||
from .core import SRGB, GREYSCALE
|
from .core import SRGB, GREYSCALE
|
||||||
from .core import PROGRESSION_ORDER
|
from .core import PROGRESSION_ORDER, CINEMA_MODE
|
||||||
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
|
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
|
||||||
from .jp2box import Jp2kBox
|
from .jp2box import Jp2kBox
|
||||||
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
|
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
|
||||||
|
|
@ -153,6 +154,36 @@ class Jp2k(Jp2kBox):
|
||||||
msg += "profile if the file type box brand is 'jp2 '."
|
msg += "profile if the file type box brand is 'jp2 '."
|
||||||
warnings.warn(msg)
|
warnings.warn(msg)
|
||||||
|
|
||||||
|
def _set_cinema_params(self, cparams, cinema_mode, fps):
|
||||||
|
"""Populate compression parameters structure for cinema2K.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
params : ctypes struct
|
||||||
|
Corresponds to compression parameters structure used by the
|
||||||
|
library.
|
||||||
|
cinema_mode : str
|
||||||
|
Either 'cinema2k' or 'cinema4k'
|
||||||
|
fps : int
|
||||||
|
Frames per second, should be either 24 or 48.
|
||||||
|
"""
|
||||||
|
if re.match("(1.5|2.0)", version.openjpeg_version) is not None:
|
||||||
|
msg = "Writing Cinema2K or Cinema4K files is not supported with "
|
||||||
|
msg += 'openjpeg library versions less than 2.0.1.'
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
if cinema_mode == 'cinema2k':
|
||||||
|
if fps == 24:
|
||||||
|
cparams.cp_cinema = CINEMA_MODE['cinema2k_24']
|
||||||
|
elif fps == 48:
|
||||||
|
cparams.cp_cinema = CINEMA_MODE['cinema2k_48']
|
||||||
|
else:
|
||||||
|
raise IOError('Cinema2K frame rate must be either 24 or 48.')
|
||||||
|
else:
|
||||||
|
cparams.cp_cinema = CINEMA_MODE['cinema4k_24']
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def _populate_cparams(self, **kwargs):
|
def _populate_cparams(self, **kwargs):
|
||||||
"""Populate compression parameters structure from input arguments.
|
"""Populate compression parameters structure from input arguments.
|
||||||
|
|
||||||
|
|
@ -219,6 +250,14 @@ class Jp2k(Jp2kBox):
|
||||||
cparams.tcp_numlayers = 1
|
cparams.tcp_numlayers = 1
|
||||||
cparams.cp_disto_alloc = 1
|
cparams.cp_disto_alloc = 1
|
||||||
|
|
||||||
|
if 'cinema2k' in kwargs:
|
||||||
|
self._set_cinema_params(cparams, 'cinema2k', kwargs['cinema2k'])
|
||||||
|
return cparams
|
||||||
|
|
||||||
|
if 'cinema4k' in kwargs:
|
||||||
|
self._set_cinema_params(cparams, 'cinema4k', kwargs['cinema4k'])
|
||||||
|
return cparams
|
||||||
|
|
||||||
if 'cbsize' in kwargs:
|
if 'cbsize' in kwargs:
|
||||||
cparams.cblockw_init = kwargs['cbsize'][1]
|
cparams.cblockw_init = kwargs['cbsize'][1]
|
||||||
cparams.cblockh_init = kwargs['cbsize'][0]
|
cparams.cblockh_init = kwargs['cbsize'][0]
|
||||||
|
|
@ -298,6 +337,10 @@ class Jp2k(Jp2kBox):
|
||||||
colorspace : int
|
colorspace : int
|
||||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
Either CLRSPC_SRGB or CLRSPC_GRAY
|
||||||
"""
|
"""
|
||||||
|
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)
|
||||||
|
|
||||||
if 'cratios' in kwargs and 'psnr' in kwargs:
|
if 'cratios' in kwargs and 'psnr' in kwargs:
|
||||||
msg = "Cannot specify cratios and psnr together."
|
msg = "Cannot specify cratios and psnr together."
|
||||||
|
|
@ -340,6 +383,10 @@ class Jp2k(Jp2kBox):
|
||||||
Image data to be written to file.
|
Image data to be written to file.
|
||||||
cbsize : tuple, optional
|
cbsize : tuple, optional
|
||||||
Code block size (DY, DX).
|
Code block size (DY, DX).
|
||||||
|
cinema2k : int, optional
|
||||||
|
frames per second, either 24 or 48
|
||||||
|
cinema4k : bool, optional
|
||||||
|
Set to True to specify Cinema4K mode, defaults to false.
|
||||||
colorspace : str, optional
|
colorspace : str, optional
|
||||||
Either 'rgb' or 'gray'.
|
Either 'rgb' or 'gray'.
|
||||||
cratios : iterable
|
cratios : iterable
|
||||||
|
|
@ -395,7 +442,7 @@ class Jp2k(Jp2kBox):
|
||||||
If glymur is unable to load the openjp2 library.
|
If glymur is unable to load the openjp2 library.
|
||||||
"""
|
"""
|
||||||
if opj2.OPENJP2 is not None:
|
if opj2.OPENJP2 is not None:
|
||||||
self._write_openjp2(img_array, verbose=verbose, **kwargs)
|
self._write_openjp2(img_array, verbose=verbose, **kwargs)
|
||||||
elif opj.OPENJPEG is not None:
|
elif opj.OPENJPEG is not None:
|
||||||
self._write_openjpeg(img_array, verbose=verbose, **kwargs)
|
self._write_openjpeg(img_array, verbose=verbose, **kwargs)
|
||||||
else:
|
else:
|
||||||
|
|
@ -473,7 +520,7 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
def _write_openjp2(self, img_array, verbose=False, **kwargs):
|
def _write_openjp2(self, img_array, verbose=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Write JPEG 2000 file using OpenJPEG 1.5 interface.
|
Write JPEG 2000 file using OpenJPEG 2.0 interface.
|
||||||
"""
|
"""
|
||||||
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
||||||
|
|
||||||
|
|
@ -559,7 +606,12 @@ class Jp2k(Jp2kBox):
|
||||||
self.parse()
|
self.parse()
|
||||||
|
|
||||||
def wrap(self, filename, boxes=None):
|
def wrap(self, filename, boxes=None):
|
||||||
"""Write the codestream back out to file, wrapped in new JP2 jacket.
|
"""Create a new JP2/JPX file wrapped in a new set of JP2 boxes.
|
||||||
|
|
||||||
|
This method is primarily aimed at wrapping a raw codestream in a set of
|
||||||
|
of JP2 boxes (turning it into a JP2 file instead of just a raw
|
||||||
|
codestream), or rewrapping a codestream in a JP2 file in a new "jacket"
|
||||||
|
of JP2 boxes.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
@ -569,6 +621,8 @@ class Jp2k(Jp2kBox):
|
||||||
JP2 box definitions to define the JP2 file format. If not
|
JP2 box definitions to define the JP2 file format. If not
|
||||||
provided, a default ""jacket" is assumed, consisting of JP2
|
provided, a default ""jacket" is assumed, consisting of JP2
|
||||||
signature, file type, JP2 header, and contiguous codestream boxes.
|
signature, file type, JP2 header, and contiguous codestream boxes.
|
||||||
|
A JPX file rewrapped without the boxes argument results in a JP2
|
||||||
|
file encompassing the first codestream.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
|
|
@ -584,19 +638,7 @@ class Jp2k(Jp2kBox):
|
||||||
>>> jp2 = j2k.wrap(tfile.name)
|
>>> jp2 = j2k.wrap(tfile.name)
|
||||||
"""
|
"""
|
||||||
if boxes is None:
|
if boxes is None:
|
||||||
# Try to create a reasonable default.
|
boxes = self._get_default_jp2_boxes()
|
||||||
boxes = [JPEG2000SignatureBox(),
|
|
||||||
FileTypeBox(),
|
|
||||||
JP2HeaderBox(),
|
|
||||||
ContiguousCodestreamBox()]
|
|
||||||
codestream = self.get_codestream()
|
|
||||||
height = codestream.segment[1].ysiz
|
|
||||||
width = codestream.segment[1].xsiz
|
|
||||||
num_components = len(codestream.segment[1].xrsiz)
|
|
||||||
boxes[2].box = [ImageHeaderBox(height=height,
|
|
||||||
width=width,
|
|
||||||
num_components=num_components),
|
|
||||||
ColourSpecificationBox(colorspace=SRGB)]
|
|
||||||
|
|
||||||
_validate_jp2_box_sequence(boxes)
|
_validate_jp2_box_sequence(boxes)
|
||||||
|
|
||||||
|
|
@ -605,34 +647,92 @@ class Jp2k(Jp2kBox):
|
||||||
if box.box_id != 'jp2c':
|
if box.box_id != 'jp2c':
|
||||||
box.write(ofile)
|
box.write(ofile)
|
||||||
else:
|
else:
|
||||||
# The codestream gets written last.
|
self._write_wrapped_codestream(ofile, box)
|
||||||
if len(self.box) == 0:
|
|
||||||
# Am I a raw codestream? If so, then it is pretty
|
|
||||||
# easy, just write the codestream box header plus all
|
|
||||||
# of myself out to file.
|
|
||||||
ofile.write(struct.pack('>I', self.length + 8))
|
|
||||||
ofile.write('jp2c'.encode())
|
|
||||||
with open(self.filename, 'rb') as ifile:
|
|
||||||
ofile.write(ifile.read())
|
|
||||||
else:
|
|
||||||
# OK, I'm a jp2 file. Need to find out where the
|
|
||||||
# raw codestream actually starts.
|
|
||||||
jp2c = [box for box in self.box
|
|
||||||
if box.box_id == 'jp2c']
|
|
||||||
jp2c = jp2c[0]
|
|
||||||
ofile.write(struct.pack('>I', jp2c.length))
|
|
||||||
ofile.write('jp2c'.encode())
|
|
||||||
with open(self.filename, 'rb') as ifile:
|
|
||||||
# Seek 8 bytes past the L, T fields to get to the
|
|
||||||
# raw codestream.
|
|
||||||
ifile.seek(jp2c.offset + 8)
|
|
||||||
ofile.write(ifile.read(jp2c.length - 8))
|
|
||||||
|
|
||||||
ofile.flush()
|
ofile.flush()
|
||||||
|
|
||||||
jp2 = Jp2k(filename)
|
jp2 = Jp2k(filename)
|
||||||
return jp2
|
return jp2
|
||||||
|
|
||||||
|
def _write_wrapped_codestream(self, ofile, box):
|
||||||
|
"""Write wrapped codestream."""
|
||||||
|
# Codestreams require a bit more care.
|
||||||
|
# Am I a raw codestream?
|
||||||
|
if len(self.box) == 0:
|
||||||
|
# Yes, just write the codestream box header plus all
|
||||||
|
# of myself out to file.
|
||||||
|
ofile.write(struct.pack('>I', self.length + 8))
|
||||||
|
ofile.write(b'jp2c')
|
||||||
|
with open(self.filename, 'rb') as ifile:
|
||||||
|
ofile.write(ifile.read())
|
||||||
|
return
|
||||||
|
|
||||||
|
# OK, I'm a jp2/jpx file. Need to find out where the raw codestream
|
||||||
|
# actually starts.
|
||||||
|
offset = box.offset
|
||||||
|
if offset == -1:
|
||||||
|
if self.box[1].brand == 'jpx ':
|
||||||
|
msg = "The codestream box must have its offset and "
|
||||||
|
msg += "length attributes fully specified if the file "
|
||||||
|
msg += "type brand is JPX."
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
|
# Find the first codestream in the file.
|
||||||
|
jp2c = [box for box in self.box if box.box_id == 'jp2c']
|
||||||
|
offset = jp2c[0].offset
|
||||||
|
|
||||||
|
# Ready to write the codestream.
|
||||||
|
with open(self.filename, 'rb') as ifile:
|
||||||
|
ifile.seek(offset)
|
||||||
|
|
||||||
|
# Verify that the specified codestream is right.
|
||||||
|
read_buffer = ifile.read(8)
|
||||||
|
L, T = struct.unpack_from('>I4s', read_buffer, 0)
|
||||||
|
if T != b'jp2c':
|
||||||
|
msg = "Unable to locate the specified codestream."
|
||||||
|
raise IOError(msg)
|
||||||
|
if L == 0:
|
||||||
|
# The length of the box is presumed to last until the end of
|
||||||
|
# the file. Compute the effective length of the box.
|
||||||
|
L = os.path.getsize(ifile.name) - ifile.tell() + 8
|
||||||
|
|
||||||
|
elif L == 1:
|
||||||
|
# The length of the box is in the XL field, a 64-bit value.
|
||||||
|
read_buffer = ifile.read(8)
|
||||||
|
L, = struct.unpack('>Q', read_buffer)
|
||||||
|
|
||||||
|
ifile.seek(offset)
|
||||||
|
read_buffer = ifile.read(L)
|
||||||
|
ofile.write(read_buffer)
|
||||||
|
|
||||||
|
def _get_default_jp2_boxes(self):
|
||||||
|
"""Create a default set of JP2 boxes."""
|
||||||
|
# Try to create a reasonable default.
|
||||||
|
boxes = [JPEG2000SignatureBox(),
|
||||||
|
FileTypeBox(),
|
||||||
|
JP2HeaderBox(),
|
||||||
|
ContiguousCodestreamBox()]
|
||||||
|
codestream = self.get_codestream()
|
||||||
|
height = codestream.segment[1].ysiz
|
||||||
|
width = codestream.segment[1].xsiz
|
||||||
|
num_components = len(codestream.segment[1].xrsiz)
|
||||||
|
if num_components < 3:
|
||||||
|
colorspace = GREYSCALE
|
||||||
|
else:
|
||||||
|
if len(self.box) == 0:
|
||||||
|
# Best guess is SRGB
|
||||||
|
colorspace = SRGB
|
||||||
|
else:
|
||||||
|
# Take whatever the first jp2 header / color specification
|
||||||
|
# says.
|
||||||
|
jp2hs = [box for box in self.box if box.box_id == 'jp2h']
|
||||||
|
colorspace = jp2hs[0].box[1].colorspace
|
||||||
|
|
||||||
|
boxes[2].box = [ImageHeaderBox(height=height, width=width,
|
||||||
|
num_components=num_components),
|
||||||
|
ColourSpecificationBox(colorspace=colorspace)]
|
||||||
|
|
||||||
|
return boxes
|
||||||
|
|
||||||
def read(self, **kwargs):
|
def read(self, **kwargs):
|
||||||
"""Read a JPEG 2000 image.
|
"""Read a JPEG 2000 image.
|
||||||
|
|
||||||
|
|
@ -649,6 +749,9 @@ class Jp2k(Jp2kBox):
|
||||||
(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.
|
||||||
|
ignore_pclr_cmap_cdef : bool
|
||||||
|
Whether or not to ignore the pclr, cmap, or cdef boxes during any
|
||||||
|
color transformation. Defaults to False.
|
||||||
verbose : bool, optional
|
verbose : bool, optional
|
||||||
Print informational messages produced by the OpenJPEG library.
|
Print informational messages produced by the OpenJPEG library.
|
||||||
|
|
||||||
|
|
@ -703,7 +806,8 @@ class Jp2k(Jp2kBox):
|
||||||
msg += "the read_bands method instead."
|
msg += "the read_bands method instead."
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
def _read_openjpeg(self, rlevel=0, verbose=False):
|
def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False,
|
||||||
|
verbose=False):
|
||||||
"""Read a JPEG 2000 image using libopenjpeg.
|
"""Read a JPEG 2000 image using libopenjpeg.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -711,6 +815,9 @@ class Jp2k(Jp2kBox):
|
||||||
rlevel : int, optional
|
rlevel : int, optional
|
||||||
Factor by which to rlevel output resolution. Use -1 to get the
|
Factor by which to rlevel output resolution. Use -1 to get the
|
||||||
lowest resolution thumbnail.
|
lowest resolution thumbnail.
|
||||||
|
ignore_pclr_cmap_cdef : bool
|
||||||
|
Whether or not to ignore the pclr, cmap, or cdef boxes during any
|
||||||
|
color transformation. Defaults to False.
|
||||||
verbose : bool, optional
|
verbose : bool, optional
|
||||||
Print informational messages produced by the OpenJPEG library.
|
Print informational messages produced by the OpenJPEG library.
|
||||||
|
|
||||||
|
|
@ -726,32 +833,12 @@ class Jp2k(Jp2kBox):
|
||||||
"""
|
"""
|
||||||
self._subsampling_sanity_check()
|
self._subsampling_sanity_check()
|
||||||
|
|
||||||
if rlevel != 0:
|
dparameters = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef)
|
||||||
# Must check the specified rlevel against the maximum.
|
|
||||||
# OpenJPEG 1.3 will segfault if rlevel is too high.
|
|
||||||
codestream = self.get_codestream()
|
|
||||||
max_rlevel = codestream.segment[2].spcod[4]
|
|
||||||
if rlevel == -1:
|
|
||||||
# -1 is shorthand for the largest rlevel
|
|
||||||
rlevel = max_rlevel
|
|
||||||
if rlevel < -1 or rlevel > max_rlevel:
|
|
||||||
msg = "rlevel must be in the range [-1, {0}] for this image."
|
|
||||||
msg = msg.format(max_rlevel)
|
|
||||||
raise IOError(msg)
|
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
try:
|
try:
|
||||||
# Set decoding parameters.
|
|
||||||
dparameters = opj.DecompressionParametersType()
|
|
||||||
opj.set_default_decoder_parameters(ctypes.byref(dparameters))
|
|
||||||
dparameters.cp_reduce = rlevel
|
|
||||||
dparameters.decod_format = self._codec_format
|
dparameters.decod_format = self._codec_format
|
||||||
|
|
||||||
infile = self.filename.encode()
|
|
||||||
nelts = opj.PATH_LEN - len(infile)
|
|
||||||
infile += b'0' * nelts
|
|
||||||
dparameters.infile = infile
|
|
||||||
|
|
||||||
dinfo = opj.create_decompress(dparameters.decod_format)
|
dinfo = opj.create_decompress(dparameters.decod_format)
|
||||||
|
|
||||||
event_mgr = opj.EventMgrType()
|
event_mgr = opj.EventMgrType()
|
||||||
|
|
@ -788,7 +875,7 @@ class Jp2k(Jp2kBox):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None,
|
def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None,
|
||||||
verbose=False):
|
verbose=False, ignore_pclr_cmap_cdef=False):
|
||||||
"""Read a JPEG 2000 image using libopenjp2.
|
"""Read a JPEG 2000 image using libopenjp2.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -818,7 +905,8 @@ class Jp2k(Jp2kBox):
|
||||||
"""
|
"""
|
||||||
self._subsampling_sanity_check()
|
self._subsampling_sanity_check()
|
||||||
|
|
||||||
dparam = self._populate_dparam(layer, rlevel, area, tile)
|
dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef,
|
||||||
|
layer=layer, tile=tile, area=area)
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
if hasattr(opj2.OPENJP2,
|
if hasattr(opj2.OPENJP2,
|
||||||
|
|
@ -862,27 +950,35 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
return img_array
|
return img_array
|
||||||
|
|
||||||
def _populate_dparam(self, layer, rlevel, area, tile):
|
def _populate_dparam(self, rlevel, ignore_pclr_cmap_cdef, tile=None,
|
||||||
|
layer=None, area=None):
|
||||||
"""Populate decompression structure with appropriate input parameters.
|
"""Populate decompression structure with appropriate input parameters.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
layer : int, optional
|
layer : int
|
||||||
Number of quality layer to decode.
|
Number of quality layer to decode.
|
||||||
rlevel : int, optional
|
rlevel : int
|
||||||
Factor by which to rlevel output resolution.
|
Factor by which to rlevel output resolution.
|
||||||
area : tuple, optional
|
area : tuple
|
||||||
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
|
||||||
Number of tile to decode.
|
Number of tile to decode.
|
||||||
|
ignore_pclr_cmap_cdef : bool
|
||||||
|
Whether or not to ignore the pclr, cmap, or cdef boxes during any
|
||||||
|
color transformation. Defaults to False.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
dparam : DecompressionParametersType (ctypes)
|
dparam : DecompressionParametersType (ctypes)
|
||||||
Corresponds to openjp2 decompression parameters structure.
|
Corresponds to openjp2 decompression parameters structure.
|
||||||
"""
|
"""
|
||||||
dparam = opj2.set_default_decoder_parameters()
|
if opj2.OPENJP2 is not None:
|
||||||
|
dparam = opj2.set_default_decoder_parameters()
|
||||||
|
else:
|
||||||
|
dparam = opj.DecompressionParametersType()
|
||||||
|
opj.set_default_decoder_parameters(ctypes.byref(dparam))
|
||||||
|
|
||||||
infile = self.filename.encode()
|
infile = self.filename.encode()
|
||||||
nelts = opj2.PATH_LEN - len(infile)
|
nelts = opj2.PATH_LEN - len(infile)
|
||||||
|
|
@ -891,12 +987,22 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
dparam.decod_format = self._codec_format
|
dparam.decod_format = self._codec_format
|
||||||
|
|
||||||
dparam.cp_layer = layer
|
if layer is not None:
|
||||||
|
dparam.cp_layer = layer
|
||||||
|
|
||||||
if rlevel == -1:
|
# Must check the specified rlevel against the maximum.
|
||||||
# Get the lowest resolution thumbnail.
|
if rlevel != 0:
|
||||||
|
# Must check the specified rlevel against the maximum.
|
||||||
codestream = self.get_codestream()
|
codestream = self.get_codestream()
|
||||||
rlevel = codestream.segment[2].spcod[4]
|
max_rlevel = codestream.segment[2].spcod[4]
|
||||||
|
if rlevel == -1:
|
||||||
|
# -1 is shorthand for the largest rlevel
|
||||||
|
rlevel = max_rlevel
|
||||||
|
elif rlevel < -1 or rlevel > max_rlevel:
|
||||||
|
msg = "rlevel must be in the range [-1, {0}] for this image."
|
||||||
|
msg = msg.format(max_rlevel)
|
||||||
|
raise IOError(msg)
|
||||||
|
|
||||||
dparam.cp_reduce = rlevel
|
dparam.cp_reduce = rlevel
|
||||||
|
|
||||||
if area is not None:
|
if area is not None:
|
||||||
|
|
@ -913,10 +1019,14 @@ class Jp2k(Jp2kBox):
|
||||||
dparam.tile_index = tile
|
dparam.tile_index = tile
|
||||||
dparam.nb_tile_to_decode = 1
|
dparam.nb_tile_to_decode = 1
|
||||||
|
|
||||||
|
if ignore_pclr_cmap_cdef is True:
|
||||||
|
# Return raw codestream components.
|
||||||
|
dparam.flags |= 1
|
||||||
|
|
||||||
return dparam
|
return dparam
|
||||||
|
|
||||||
def read_bands(self, rlevel=0, layer=0, area=None, tile=None,
|
def read_bands(self, rlevel=0, layer=0, area=None, tile=None,
|
||||||
verbose=False):
|
verbose=False, ignore_pclr_cmap_cdef=False):
|
||||||
"""Read a JPEG 2000 image.
|
"""Read a JPEG 2000 image.
|
||||||
|
|
||||||
The only time you should use this method is when the image has
|
The only time you should use this method is when the image has
|
||||||
|
|
@ -934,6 +1044,9 @@ class Jp2k(Jp2kBox):
|
||||||
(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.
|
||||||
|
ignore_pclr_cmap_cdef : bool
|
||||||
|
Whether or not to ignore the pclr, cmap, or cdef boxes during any
|
||||||
|
color transformation. Defaults to False.
|
||||||
verbose : bool, optional
|
verbose : bool, optional
|
||||||
Print informational messages produced by the OpenJPEG library.
|
Print informational messages produced by the OpenJPEG library.
|
||||||
|
|
||||||
|
|
@ -963,7 +1076,8 @@ class Jp2k(Jp2kBox):
|
||||||
"of OpenJP2 installed before using "
|
"of OpenJP2 installed before using "
|
||||||
"this functionality.")
|
"this functionality.")
|
||||||
|
|
||||||
dparam = self._populate_dparam(layer, rlevel, area, tile)
|
dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef,
|
||||||
|
layer=layer, tile=tile, area=area)
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
if hasattr(opj2.OPENJP2,
|
if hasattr(opj2.OPENJP2,
|
||||||
|
|
@ -1025,7 +1139,7 @@ class Jp2k(Jp2kBox):
|
||||||
>>> codestream = jp2.get_codestream()
|
>>> codestream = jp2.get_codestream()
|
||||||
>>> print(codestream.segment[1])
|
>>> print(codestream.segment[1])
|
||||||
SIZ marker segment @ (3233, 47)
|
SIZ marker segment @ (3233, 47)
|
||||||
Profile: 2
|
Profile: no profile
|
||||||
Reference Grid Height, Width: (1456 x 2592)
|
Reference Grid Height, Width: (1456 x 2592)
|
||||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||||
Reference Tile Height, Width: (1456 x 2592)
|
Reference Tile Height, Width: (1456 x 2592)
|
||||||
|
|
@ -1125,12 +1239,26 @@ def _validate_jp2_box_sequence(boxes):
|
||||||
if boxes[1].brand == 'jpx ':
|
if boxes[1].brand == 'jpx ':
|
||||||
_validate_jpx_box_sequence(boxes)
|
_validate_jpx_box_sequence(boxes)
|
||||||
else:
|
else:
|
||||||
|
# Validate the JP2 box IDs.
|
||||||
count = _collect_box_count(boxes)
|
count = _collect_box_count(boxes)
|
||||||
for id in count.keys():
|
for box_id in count.keys():
|
||||||
if 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 type "
|
||||||
msg += "brand be set to 'jpx '."
|
msg += "brand be set to 'jpx '."
|
||||||
raise IOError(msg.format(id))
|
raise IOError(msg.format(box_id))
|
||||||
|
|
||||||
|
_validate_jp2_colr(boxes)
|
||||||
|
|
||||||
|
def _validate_jp2_colr(boxes):
|
||||||
|
"""
|
||||||
|
Validate JP2 requirements on colour specification boxes.
|
||||||
|
"""
|
||||||
|
lst = [box for box in boxes if box.box_id == 'jp2h']
|
||||||
|
jp2h = lst[0]
|
||||||
|
for colr in [box for box in jp2h.box if box.box_id == 'colr']:
|
||||||
|
if colr.approximation != 0:
|
||||||
|
msg = "A JP2 colr box cannot have a non-zero approximation field."
|
||||||
|
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."""
|
||||||
|
|
@ -1231,7 +1359,7 @@ 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])
|
||||||
intersection = box_ids.intersection(JP2H_CHILDREN)
|
intersection = box_ids.intersection(JP2H_CHILDREN)
|
||||||
if len(intersection) > 0 and parent_box_name != 'jp2h':
|
if len(intersection) > 0 and parent_box_name not in ['jp2h', 'jpch']:
|
||||||
msg = "A '{0}' box can only be nested in a JP2 header box."
|
msg = "A '{0}' box can only be nested in a JP2 header box."
|
||||||
raise IOError(msg.format(list(intersection)[0]))
|
raise IOError(msg.format(list(intersection)[0]))
|
||||||
|
|
||||||
|
|
@ -1507,6 +1635,10 @@ def _populate_image_struct(cparams, image, imgdata):
|
||||||
|
|
||||||
# Stage the image data to the openjpeg data structure.
|
# Stage the image data to the openjpeg data structure.
|
||||||
for k in range(0, num_comps):
|
for k in range(0, num_comps):
|
||||||
|
if cparams.cp_cinema:
|
||||||
|
image.contents.comps[k].prec = 12
|
||||||
|
image.contents.comps[k].bpp = 12
|
||||||
|
|
||||||
layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32)
|
layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32)
|
||||||
dest = image.contents.comps[k].data
|
dest = image.contents.comps[k].data
|
||||||
src = layer.ctypes.data
|
src = layer.ctypes.data
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,20 @@ import sys
|
||||||
from .config import glymur_config
|
from .config import glymur_config
|
||||||
OPENJP2, OPENJPEG = glymur_config()
|
OPENJP2, OPENJPEG = glymur_config()
|
||||||
|
|
||||||
|
def version():
|
||||||
|
"""Wrapper for opj_version library routine."""
|
||||||
|
OPENJP2.opj_version.restype = ctypes.c_char_p
|
||||||
|
library_version = OPENJP2.opj_version()
|
||||||
|
if sys.hexversion >= 0x03000000:
|
||||||
|
return library_version.decode('utf-8')
|
||||||
|
else:
|
||||||
|
return library_version
|
||||||
|
|
||||||
|
if OPENJP2 is not None:
|
||||||
|
_MAJOR, _MINOR, _PATCH = version().split('.')
|
||||||
|
else:
|
||||||
|
_MINOR = 0
|
||||||
|
|
||||||
ERROR_MSG_LST = []
|
ERROR_MSG_LST = []
|
||||||
|
|
||||||
# Map certain atomic OpenJPEG datatypes to the ctypes equivalents.
|
# Map certain atomic OpenJPEG datatypes to the ctypes equivalents.
|
||||||
|
|
@ -35,6 +49,7 @@ CLRSPC_UNSPECIFIED = 0
|
||||||
CLRSPC_SRGB = 1
|
CLRSPC_SRGB = 1
|
||||||
CLRSPC_GRAY = 2
|
CLRSPC_GRAY = 2
|
||||||
CLRSPC_YCC = 3
|
CLRSPC_YCC = 3
|
||||||
|
CLRSPC_EYCC = 4
|
||||||
COLOR_SPACE_TYPE = ctypes.c_int
|
COLOR_SPACE_TYPE = ctypes.c_int
|
||||||
|
|
||||||
# supported codec
|
# supported codec
|
||||||
|
|
@ -392,6 +407,8 @@ class ImageCompType(ctypes.Structure):
|
||||||
# image component data
|
# image component data
|
||||||
("data", ctypes.POINTER(ctypes.c_int32))]
|
("data", ctypes.POINTER(ctypes.c_int32))]
|
||||||
|
|
||||||
|
if _MINOR == '1':
|
||||||
|
_fields_.append(("alpha", ctypes.c_uint16))
|
||||||
|
|
||||||
class ImageType(ctypes.Structure):
|
class ImageType(ctypes.Structure):
|
||||||
"""Defines image data and characteristics.
|
"""Defines image data and characteristics.
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ try:
|
||||||
HAS_PYTHON_XMP_TOOLKIT = True
|
HAS_PYTHON_XMP_TOOLKIT = True
|
||||||
else:
|
else:
|
||||||
HAS_PYTHON_XMP_TOOLKIT = False
|
HAS_PYTHON_XMP_TOOLKIT = False
|
||||||
except ImportError:
|
except:
|
||||||
HAS_PYTHON_XMP_TOOLKIT = False
|
HAS_PYTHON_XMP_TOOLKIT = False
|
||||||
|
|
||||||
# Need to know of the libopenjp2 version is the official 2.0.0 release and NOT
|
# Need to know of the libopenjp2 version is the official 2.0.0 release and NOT
|
||||||
|
|
@ -412,7 +412,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
|
||||||
Main header:
|
Main header:
|
||||||
SOC marker segment @ (3231, 0)
|
SOC marker segment @ (3231, 0)
|
||||||
SIZ marker segment @ (3233, 47)
|
SIZ marker segment @ (3233, 47)
|
||||||
Profile: 2
|
Profile: no profile
|
||||||
Reference Grid Height, Width: (1456 x 2592)
|
Reference Grid Height, Width: (1456 x 2592)
|
||||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||||
Reference Tile Height, Width: (1456 x 2592)
|
Reference Tile Height, Width: (1456 x 2592)
|
||||||
|
|
@ -477,7 +477,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
|
||||||
Main header:
|
Main header:
|
||||||
SOC marker segment @ (3231, 0)
|
SOC marker segment @ (3231, 0)
|
||||||
SIZ marker segment @ (3233, 47)
|
SIZ marker segment @ (3233, 47)
|
||||||
Profile: 2
|
Profile: no profile
|
||||||
Reference Grid Height, Width: (1456 x 2592)
|
Reference Grid Height, Width: (1456 x 2592)
|
||||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||||
Reference Tile Height, Width: (1456 x 2592)
|
Reference Tile Height, Width: (1456 x 2592)
|
||||||
|
|
@ -554,15 +554,21 @@ UUID Box (uuid) @ (77, 3146)
|
||||||
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
|
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
|
||||||
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
|
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
|
||||||
|
|
||||||
# Output of reader requirement printing for file7.jp2
|
# Output of reader requirements printing for text_GBR.jp2
|
||||||
file7_rreq = r"""Reader Requirements Box (rreq) @ (44, 24)
|
text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109)
|
||||||
Fully Understands Aspect Mask: 0xa0
|
Fully Understands Aspect Mask: 0xffff
|
||||||
Display Completely Mask: 0xc0
|
Display Completely Mask: 0xf8f0
|
||||||
Standard Features and Masks:
|
Standard Features and Masks:
|
||||||
Feature 005: 0x80 Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 | ISO/IEC 15444-1
|
Feature 001: 0x8000 Deprecated - contains no extensions
|
||||||
Feature 060: 0x60 e-sRGB enumerated colorspace
|
Feature 005: 0x4080 Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 | ISO/IEC 15444-1
|
||||||
Feature 043: 0x40 Deprecated - compositing layer uses restricted ICC profile
|
Feature 012: 0x2040 Deprecated - codestream is contiguous
|
||||||
Vendor Features:"""
|
Feature 018: 0x1020 Deprecated - support for compositing is not required
|
||||||
|
Feature 044: 0x810 Compositing layer uses Any ICC profile
|
||||||
|
Vendor Features:
|
||||||
|
UUID 3a0d0218-0ae9-4115-b376-4bca41ce0e71
|
||||||
|
UUID 47c92ccc-d1a1-4581-b904-38bb5467713b
|
||||||
|
UUID bc45a774-dd50-4ec6-a9f6-f3a137f47e90
|
||||||
|
UUID d7c8c5ef-951f-43b2-8757-042500f538e8"""
|
||||||
|
|
||||||
file1_xml = """XML Box (xml ) @ (36, 439)
|
file1_xml = """XML Box (xml ) @ (36, 439)
|
||||||
<IMAGE_CREATION xmlns="http://www.jpeg.org/jpx/1.0/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jpeg.org/jpx/1.0/xml http://www.jpeg.org/metadata/15444-2.xsd">
|
<IMAGE_CREATION xmlns="http://www.jpeg.org/jpx/1.0/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jpeg.org/jpx/1.0/xml http://www.jpeg.org/metadata/15444-2.xsd">
|
||||||
|
|
@ -571,3 +577,48 @@ file1_xml = """XML Box (xml ) @ (36, 439)
|
||||||
\t\t<IMAGE_SOURCE>Professional 120 Image</IMAGE_SOURCE>
|
\t\t<IMAGE_SOURCE>Professional 120 Image</IMAGE_SOURCE>
|
||||||
\t</GENERAL_CREATION_INFO>
|
\t</GENERAL_CREATION_INFO>
|
||||||
</IMAGE_CREATION>"""
|
</IMAGE_CREATION>"""
|
||||||
|
|
||||||
|
issue_182_cmap = """Component Mapping Box (cmap) @ (130, 24)
|
||||||
|
Component 0 ==> palette column 0
|
||||||
|
Component 0 ==> palette column 1
|
||||||
|
Component 0 ==> palette column 2
|
||||||
|
Component 0 ==> palette column 3"""
|
||||||
|
|
||||||
|
issue_183_colr = """Colour Specification Box (colr) @ (62, 12)
|
||||||
|
Method: restricted ICC profile
|
||||||
|
Precedence: 0
|
||||||
|
ICC Profile: None"""
|
||||||
|
|
||||||
|
|
||||||
|
# Progression order is invalid.
|
||||||
|
issue_186_progression_order = """COD marker segment @ (174, 12)
|
||||||
|
Coding style:
|
||||||
|
Entropy coder, without partitions
|
||||||
|
SOP marker segments: False
|
||||||
|
EPH marker segments: False
|
||||||
|
Coding style parameters:
|
||||||
|
Progression order: 33 (invalid)
|
||||||
|
Number of layers: 1
|
||||||
|
Multiple component transformation usage: reversible
|
||||||
|
Number of resolutions: 6
|
||||||
|
Code block height, width: (32 x 32)
|
||||||
|
Wavelet transform: 9-7 irreversible
|
||||||
|
Precinct size: default, 2^15 x 2^15
|
||||||
|
Code block context:
|
||||||
|
Selective arithmetic coding bypass: False
|
||||||
|
Reset context probabilities on coding pass boundaries: False
|
||||||
|
Termination on each coding pass: False
|
||||||
|
Vertically stripe causal context: False
|
||||||
|
Predictable termination: False
|
||||||
|
Segmentation symbols: False"""
|
||||||
|
|
||||||
|
# Cinema 2K profile
|
||||||
|
cinema2k_profile = """SIZ marker segment @ (2, 47)
|
||||||
|
Profile: Cinema 2K
|
||||||
|
Reference Grid Height, Width: (1080 x 1920)
|
||||||
|
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||||
|
Reference Tile Height, Width: (1080 x 1920)
|
||||||
|
Vertical, Horizontal Reference Tile Offset: (0 x 0)
|
||||||
|
Bitdepth: (12, 12, 12)
|
||||||
|
Signed: (False, False, False)
|
||||||
|
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))"""
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,12 @@ import struct
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
from glymur import Jp2k
|
from glymur import Jp2k
|
||||||
import glymur
|
import glymur
|
||||||
|
|
||||||
try:
|
from .fixtures import opj_data_file, OPJ_DATA_ROOT
|
||||||
DATA_ROOT = os.environ['OPJ_DATA_ROOT']
|
|
||||||
except KeyError:
|
|
||||||
DATA_ROOT = None
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class TestCodestream(unittest.TestCase):
|
class TestCodestream(unittest.TestCase):
|
||||||
"""Test suite for unusual codestream cases."""
|
"""Test suite for unusual codestream cases."""
|
||||||
|
|
@ -34,8 +29,73 @@ class TestCodestream(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
def test_siz_segment_ssiz_unsigned(self):
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"""ssiz attribute to be removed in future release"""
|
||||||
|
j = Jp2k(self.jp2file)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
|
||||||
|
# The ssiz attribute was simply a tuple of raw bytes.
|
||||||
|
# The first 7 bits are interpreted as the bitdepth, the MSB determines
|
||||||
|
# whether or not it is signed.
|
||||||
|
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
class TestCodestreamOpjData(unittest.TestCase):
|
||||||
|
"""Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.jp2file = glymur.data.nemo()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_bad_rsiz(self):
|
||||||
|
"""Should warn if RSIZ is bad. Issue196"""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
j = Jp2k(filename)
|
||||||
|
|
||||||
|
def test_bad_wavelet_transform(self):
|
||||||
|
"""Should warn if wavelet transform is bad. Issue195"""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
j = Jp2k(filename)
|
||||||
|
|
||||||
|
def test_invalid_progression_order(self):
|
||||||
|
"""Should still be able to parse even if prog order is invalid."""
|
||||||
|
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
Jp2k(jfile)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
Jp2k(jfile)
|
||||||
|
|
||||||
|
def test_tile_height_is_zero(self):
|
||||||
|
"""Zero tile height should not cause an exception."""
|
||||||
|
filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
Jp2k(filename)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
def test_reserved_marker_segment(self):
|
def test_reserved_marker_segment(self):
|
||||||
"""Reserved marker segments are ok."""
|
"""Reserved marker segments are ok."""
|
||||||
|
|
@ -45,7 +105,7 @@ class TestCodestream(unittest.TestCase):
|
||||||
#
|
#
|
||||||
# Let's inject a reserved marker segment into a file that
|
# Let's inject a reserved marker segment into a file that
|
||||||
# we know something about to make sure we can still parse it.
|
# we know something about to make sure we can still parse it.
|
||||||
filename = os.path.join(DATA_ROOT, 'input/conformance/p0_01.j2k')
|
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_01.j2k')
|
||||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
with open(filename, 'rb') as ifile:
|
with open(filename, 'rb') as ifile:
|
||||||
# Everything up until the first QCD marker.
|
# Everything up until the first QCD marker.
|
||||||
|
|
@ -67,8 +127,6 @@ class TestCodestream(unittest.TestCase):
|
||||||
self.assertEqual(codestream.segment[2].length, 3)
|
self.assertEqual(codestream.segment[2].length, 3)
|
||||||
self.assertEqual(codestream.segment[2].data, b'\x00')
|
self.assertEqual(codestream.segment[2].data, b'\x00')
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||||
"Uses features introduced in 3.2.")
|
"Uses features introduced in 3.2.")
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
|
|
@ -77,7 +135,7 @@ class TestCodestream(unittest.TestCase):
|
||||||
# Let's inject a marker segment whose marker does not appear to
|
# Let's inject a marker segment whose marker does not appear to
|
||||||
# be valid. We still parse the file, but warn about the offending
|
# be valid. We still parse the file, but warn about the offending
|
||||||
# marker.
|
# marker.
|
||||||
filename = os.path.join(DATA_ROOT, 'input/conformance/p0_01.j2k')
|
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_01.j2k')
|
||||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
with open(filename, 'rb') as ifile:
|
with open(filename, 'rb') as ifile:
|
||||||
# Everything up until the first QCD marker.
|
# Everything up until the first QCD marker.
|
||||||
|
|
@ -100,11 +158,9 @@ class TestCodestream(unittest.TestCase):
|
||||||
self.assertEqual(codestream.segment[2].length, 3)
|
self.assertEqual(codestream.segment[2].length, 3)
|
||||||
self.assertEqual(codestream.segment[2].data, b'\x00')
|
self.assertEqual(codestream.segment[2].data, b'\x00')
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_psot_is_zero(self):
|
def test_psot_is_zero(self):
|
||||||
"""Psot=0 in SOT is perfectly legal. Issue #78."""
|
"""Psot=0 in SOT is perfectly legal. Issue #78."""
|
||||||
filename = os.path.join(DATA_ROOT,
|
filename = os.path.join(OPJ_DATA_ROOT,
|
||||||
'input/nonregression/123.j2c')
|
'input/nonregression/123.j2c')
|
||||||
j = Jp2k(filename)
|
j = Jp2k(filename)
|
||||||
codestream = j.get_codestream(header_only=False)
|
codestream = j.get_codestream(header_only=False)
|
||||||
|
|
@ -114,22 +170,9 @@ class TestCodestream(unittest.TestCase):
|
||||||
self.assertEqual(codestream.segment[-1].marker_id, 'EOC')
|
self.assertEqual(codestream.segment[-1].marker_id, 'EOC')
|
||||||
|
|
||||||
|
|
||||||
def test_siz_segment_ssiz_unsigned(self):
|
|
||||||
"""ssiz attribute to be removed in future release"""
|
|
||||||
j = Jp2k(self.jp2file)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
|
|
||||||
# The ssiz attribute was simply a tuple of raw bytes.
|
|
||||||
# The first 7 bits are interpreted as the bitdepth, the MSB determines
|
|
||||||
# whether or not it is signed.
|
|
||||||
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_siz_segment_ssiz_signed(self):
|
def test_siz_segment_ssiz_signed(self):
|
||||||
"""ssiz attribute to be removed in future release"""
|
"""ssiz attribute to be removed in future release"""
|
||||||
filename = os.path.join(DATA_ROOT, 'input/conformance/p0_03.j2k')
|
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_03.j2k')
|
||||||
j = Jp2k(filename)
|
j = Jp2k(filename)
|
||||||
codestream = j.get_codestream()
|
codestream = j.get_codestream()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
"""
|
|
||||||
These tests deal with JPX/JP2/J2K images in the format-corpus repository.
|
|
||||||
"""
|
|
||||||
# R0904: Not too many methods in unittest.
|
|
||||||
# pylint: disable=R0904
|
|
||||||
|
|
||||||
# E1101: assertWarns introduced in python 3.2
|
|
||||||
# pylint: disable=E1101
|
|
||||||
|
|
||||||
import os
|
|
||||||
from os.path import join
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import glymur
|
|
||||||
from glymur import Jp2k
|
|
||||||
|
|
||||||
try:
|
|
||||||
FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT']
|
|
||||||
except KeyError:
|
|
||||||
FORMAT_CORPUS_DATA_ROOT = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
OPJ_DATA_ROOT = os.environ['OPJ_DATA_ROOT']
|
|
||||||
except KeyError:
|
|
||||||
OPJ_DATA_ROOT = None
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(FORMAT_CORPUS_DATA_ROOT is None,
|
|
||||||
"FORMAT_CORPUS_DATA_ROOT environment variable not set")
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
|
||||||
"Requires features introduced in 3.2 (assertWarns)")
|
|
||||||
class TestSuiteFormatCorpus(unittest.TestCase):
|
|
||||||
"""Test suite for files in format corpus repository."""
|
|
||||||
|
|
||||||
@unittest.skipIf(re.match(r"""1\.[0123]""",
|
|
||||||
glymur.version.openjpeg_version) is not None,
|
|
||||||
"Needs 1.3+ to catch this.")
|
|
||||||
def test_balloon_trunc1(self):
|
|
||||||
"""Has one byte shaved off of EOC marker."""
|
|
||||||
jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT,
|
|
||||||
'jp2k-test/byteCorruption/balloon_trunc1.jp2')
|
|
||||||
j2k = Jp2k(jfile)
|
|
||||||
with self.assertWarns(UserWarning):
|
|
||||||
codestream = j2k.get_codestream(header_only=False)
|
|
||||||
|
|
||||||
# The last segment is truncated, so there should not be an EOC marker.
|
|
||||||
self.assertNotEqual(codestream.segment[-1].marker_id, 'EOC')
|
|
||||||
|
|
||||||
# The codestream is not as long as claimed.
|
|
||||||
with self.assertRaises(OSError):
|
|
||||||
j2k.read(rlevel=-1)
|
|
||||||
|
|
||||||
@unittest.skipIf(re.match(r"""1\.[01234]""",
|
|
||||||
glymur.version.openjpeg_version) is not None,
|
|
||||||
"Needs 1.4+ to catch this.")
|
|
||||||
def test_balloon_trunc2(self):
|
|
||||||
"""Shortened by 5000 bytes."""
|
|
||||||
jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT,
|
|
||||||
'jp2k-test/byteCorruption/balloon_trunc2.jp2')
|
|
||||||
j2k = Jp2k(jfile)
|
|
||||||
with self.assertWarns(UserWarning):
|
|
||||||
codestream = j2k.get_codestream(header_only=False)
|
|
||||||
|
|
||||||
# The last segment is truncated, so there should not be an EOC marker.
|
|
||||||
self.assertNotEqual(codestream.segment[-1].marker_id, 'EOC')
|
|
||||||
|
|
||||||
# The codestream is not as long as claimed.
|
|
||||||
with self.assertRaises(OSError):
|
|
||||||
j2k.read(rlevel=-1)
|
|
||||||
|
|
||||||
def test_balloon_trunc3(self):
|
|
||||||
"""Most of last tile is missing."""
|
|
||||||
jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT,
|
|
||||||
'jp2k-test/byteCorruption/balloon_trunc3.jp2')
|
|
||||||
j2k = Jp2k(jfile)
|
|
||||||
with self.assertWarns(UserWarning):
|
|
||||||
codestream = j2k.get_codestream(header_only=False)
|
|
||||||
|
|
||||||
# The last segment is truncated, so there should not be an EOC marker.
|
|
||||||
self.assertNotEqual(codestream.segment[-1].marker_id, 'EOC')
|
|
||||||
|
|
||||||
# Should error out, it does not.
|
|
||||||
#with self.assertRaises(OSError):
|
|
||||||
# j2k.read(rlevel=-1)
|
|
||||||
|
|
||||||
def test_jp2_brand_any_icc_profile(self):
|
|
||||||
"""If 'jp2 ', then the method cannot be any icc profile."""
|
|
||||||
jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT,
|
|
||||||
'jp2k-test', 'icc',
|
|
||||||
'balloon_eciRGBv2_ps_adobeplugin.jpf')
|
|
||||||
with self.assertWarns(UserWarning):
|
|
||||||
Jp2k(jfile)
|
|
||||||
|
|
||||||
def test_jp2_brand_iccpr_mult_colr(self):
|
|
||||||
"""Has colr box, one that conforms, one that does not."""
|
|
||||||
|
|
||||||
# Wrong 'brand' field; contains two versions of ICC profile: one
|
|
||||||
# embedded using "Any ICC" method; other embedded using "Restricted
|
|
||||||
# ICC" method, with description ("Modified eciRGB v2") and profileClass
|
|
||||||
# ("Input Device") changed relative to original profile.
|
|
||||||
jfile = join(FORMAT_CORPUS_DATA_ROOT, 'jp2k-test', 'icc',
|
|
||||||
'balloon_eciRGBv2_ps_adobeplugin_jp2compatible.jpf')
|
|
||||||
with self.assertWarns(UserWarning):
|
|
||||||
Jp2k(jfile)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
|
||||||
"Requires features introduced in 3.2 (assertWarns)")
|
|
||||||
class TestSuiteOpj(unittest.TestCase):
|
|
||||||
"""Test suite for files in openjpeg repository."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_jp2_brand_any_icc_profile(self):
|
|
||||||
"""If 'jp2 ', then the method cannot be any icc profile."""
|
|
||||||
filename = os.path.join(OPJ_DATA_ROOT,
|
|
||||||
'input/nonregression/text_GBR.jp2')
|
|
||||||
with self.assertWarns(UserWarning):
|
|
||||||
Jp2k(filename)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -9,6 +9,7 @@ import datetime
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
@ -30,8 +31,11 @@ class TestICC(unittest.TestCase):
|
||||||
def test_file5(self):
|
def test_file5(self):
|
||||||
"""basic ICC profile"""
|
"""basic ICC profile"""
|
||||||
filename = opj_data_file('input/conformance/file5.jp2')
|
filename = opj_data_file('input/conformance/file5.jp2')
|
||||||
j = Jp2k(filename)
|
with warnings.catch_warnings():
|
||||||
profile = j.box[3].box[1].icc_profile
|
# The file has a bad compatibility list entry. Not important here.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = Jp2k(filename)
|
||||||
|
profile = j.box[2].box[1].icc_profile
|
||||||
self.assertEqual(profile['Size'], 546)
|
self.assertEqual(profile['Size'], 546)
|
||||||
self.assertEqual(profile['Preferred CMM Type'], 0)
|
self.assertEqual(profile['Preferred CMM Type'], 0)
|
||||||
self.assertEqual(profile['Version'], '2.2.0')
|
self.assertEqual(profile['Version'], '2.2.0')
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,23 @@ class TestDataEntryURL(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.jp2file = glymur.data.nemo()
|
self.jp2file = glymur.data.nemo()
|
||||||
|
|
||||||
|
def test_wrap_greyscale(self):
|
||||||
|
"""A single component should be wrapped as GREYSCALE."""
|
||||||
|
j = Jp2k(self.jp2file)
|
||||||
|
data = j.read()
|
||||||
|
red = data[:, :, 0]
|
||||||
|
|
||||||
|
# Write it back out as a raw codestream.
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile1:
|
||||||
|
j2k = glymur.Jp2k(tfile1.name, 'wb')
|
||||||
|
j2k.write(data[:, :, 0])
|
||||||
|
|
||||||
|
# Ok, now rewrap it as JP2. The colorspace should be GREYSCALE.
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
|
||||||
|
jp2 = j2k.wrap(tfile2.name)
|
||||||
|
self.assertEqual(jp2.box[2].box[1].colorspace,
|
||||||
|
glymur.core.GREYSCALE)
|
||||||
|
|
||||||
def test_basic_url(self):
|
def test_basic_url(self):
|
||||||
"""Just your most basic URL box."""
|
"""Just your most basic URL box."""
|
||||||
# Wrap our j2k file in a JP2 box along with an interior url box.
|
# Wrap our j2k file in a JP2 box along with an interior url box.
|
||||||
|
|
@ -340,6 +357,7 @@ class TestChannelDefinition(unittest.TestCase):
|
||||||
with self.assertRaises((IOError, OSError)):
|
with self.assertRaises((IOError, OSError)):
|
||||||
j2k.wrap(tfile.name, boxes=boxes)
|
j2k.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||||
def test_bad_type(self):
|
def test_bad_type(self):
|
||||||
"""Channel types are limited to 0, 1, 2, 65535
|
"""Channel types are limited to 0, 1, 2, 65535
|
||||||
Should reject if not all of index, channel_type, association the
|
Should reject if not all of index, channel_type, association the
|
||||||
|
|
@ -347,17 +365,18 @@ class TestChannelDefinition(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
channel_type = (COLOR, COLOR, 3)
|
channel_type = (COLOR, COLOR, 3)
|
||||||
association = (RED, GREEN, BLUE)
|
association = (RED, GREEN, BLUE)
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
|
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
|
||||||
association=association)
|
association=association)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||||
def test_wrong_lengths(self):
|
def test_wrong_lengths(self):
|
||||||
"""Should reject if not all of index, channel_type, association the
|
"""Should reject if not all of index, channel_type, association the
|
||||||
same length.
|
same length.
|
||||||
"""
|
"""
|
||||||
channel_type = (COLOR, COLOR)
|
channel_type = (COLOR, COLOR)
|
||||||
association = (RED, GREEN, BLUE)
|
association = (RED, GREEN, BLUE)
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
|
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
|
||||||
association=association)
|
association=association)
|
||||||
|
|
||||||
|
|
@ -373,14 +392,19 @@ class TestFileTypeBox(unittest.TestCase):
|
||||||
|
|
||||||
def test_brand_unknown(self):
|
def test_brand_unknown(self):
|
||||||
"""A ftyp box brand must be 'jp2 ' or 'jpx '."""
|
"""A ftyp box brand must be 'jp2 ' or 'jpx '."""
|
||||||
ftyp = glymur.jp2box.FileTypeBox(brand='jp3')
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
ftyp = glymur.jp2box.FileTypeBox(brand='jp3')
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
ftyp.write(tfile)
|
ftyp.write(tfile)
|
||||||
|
|
||||||
def test_cl_entry_unknown(self):
|
def test_cl_entry_unknown(self):
|
||||||
"""A ftyp box cl list can only contain 'jp2 ', 'jpx ', or 'jpxb'."""
|
"""A ftyp box cl list can only contain 'jp2 ', 'jpx ', or 'jpxb'."""
|
||||||
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3'])
|
with warnings.catch_warnings():
|
||||||
|
# Bad compatibility list item.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3'])
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
ftyp.write(tfile)
|
ftyp.write(tfile)
|
||||||
|
|
@ -429,6 +453,18 @@ class TestColourSpecificationBox(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
j2k.wrap(tfile.name, boxes=boxes)
|
j2k.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
|
def test_bad_approx_jp2_field(self):
|
||||||
|
"""JP2 has requirements for approx field"""
|
||||||
|
j2k = Jp2k(self.j2kfile)
|
||||||
|
boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c]
|
||||||
|
colr = ColourSpecificationBox(colorspace=glymur.core.SRGB,
|
||||||
|
approximation=1)
|
||||||
|
boxes[2].box = [self.ihdr, colr]
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
j2k.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
def test_default_colr(self):
|
def test_default_colr(self):
|
||||||
"""basic colr instantiation"""
|
"""basic colr instantiation"""
|
||||||
colr = ColourSpecificationBox(colorspace=glymur.core.SRGB)
|
colr = ColourSpecificationBox(colorspace=glymur.core.SRGB)
|
||||||
|
|
@ -438,27 +474,30 @@ class TestColourSpecificationBox(unittest.TestCase):
|
||||||
self.assertEqual(colr.colorspace, glymur.core.SRGB)
|
self.assertEqual(colr.colorspace, glymur.core.SRGB)
|
||||||
self.assertIsNone(colr.icc_profile)
|
self.assertIsNone(colr.icc_profile)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
|
||||||
def test_colr_with_cspace_and_icc(self):
|
def test_colr_with_cspace_and_icc(self):
|
||||||
"""Colour specification boxes can't have both."""
|
"""Colour specification boxes can't have both."""
|
||||||
with self.assertRaises((OSError, IOError)):
|
with self.assertWarns(UserWarning):
|
||||||
colorspace = glymur.core.SRGB
|
colorspace = glymur.core.SRGB
|
||||||
rawb = b'\x01\x02\x03\x04'
|
rawb = b'\x01\x02\x03\x04'
|
||||||
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
||||||
icc_profile=rawb)
|
icc_profile=rawb)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
|
||||||
def test_colr_with_bad_method(self):
|
def test_colr_with_bad_method(self):
|
||||||
"""colr must have a valid method field"""
|
"""colr must have a valid method field"""
|
||||||
colorspace = glymur.core.SRGB
|
colorspace = glymur.core.SRGB
|
||||||
method = -1
|
method = -1
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
||||||
method=method)
|
method=method)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
|
||||||
def test_colr_with_bad_approx(self):
|
def test_colr_with_bad_approx(self):
|
||||||
"""colr must have a valid approximation field"""
|
"""colr should have a valid approximation field"""
|
||||||
colorspace = glymur.core.SRGB
|
colorspace = glymur.core.SRGB
|
||||||
approx = -1
|
approx = -1
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
||||||
approximation=approx)
|
approximation=approx)
|
||||||
|
|
||||||
|
|
@ -484,21 +523,23 @@ class TestPaletteBox(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||||
def test_mismatched_bitdepth_signed(self):
|
def test_mismatched_bitdepth_signed(self):
|
||||||
"""bitdepth and signed arguments must have equal length"""
|
"""bitdepth and signed arguments must have equal length"""
|
||||||
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
|
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
|
||||||
bps = (8, 8, 8)
|
bps = (8, 8, 8)
|
||||||
signed = (False, False)
|
signed = (False, False)
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
||||||
signed=signed)
|
signed=signed)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||||
def test_mismatched_signed_palette(self):
|
def test_mismatched_signed_palette(self):
|
||||||
"""bitdepth and signed arguments must have equal length"""
|
"""bitdepth and signed arguments must have equal length"""
|
||||||
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
|
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
|
||||||
bps = (8, 8, 8, 8)
|
bps = (8, 8, 8, 8)
|
||||||
signed = (False, False, False, False)
|
signed = (False, False, False, False)
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
||||||
signed=signed)
|
signed=signed)
|
||||||
|
|
||||||
|
|
@ -666,8 +707,9 @@ class TestWrap(unittest.TestCase):
|
||||||
def test_jpx_to_jp2(self):
|
def test_jpx_to_jp2(self):
|
||||||
"""basic test for rewrapping a jpx file"""
|
"""basic test for rewrapping a jpx file"""
|
||||||
jpx = Jp2k(self.jpxfile)
|
jpx = Jp2k(self.jpxfile)
|
||||||
idx = [0, 1, 3, 6]
|
# Use only the signature, file type, header, and 1st codestream.
|
||||||
boxes = [jpx.box[idx] for idx in [0, 1, 3, 6]]
|
lst = [0, 1, 2, 5]
|
||||||
|
boxes = [jpx.box[idx] for idx in lst]
|
||||||
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||||
jp2 = jpx.wrap(tfile.name, boxes=boxes)
|
jp2 = jpx.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
|
@ -828,6 +870,67 @@ class TestWrap(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
j2k.wrap(tfile.name, boxes=boxes)
|
j2k.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
def test_wrap_jpx_to_jp2_with_unadorned_jpch(self):
|
||||||
|
"""A JPX file rewrapped with plain jpch is not allowed."""
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1:
|
||||||
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
boxes = [jpx.box[0], jpx.box[1], jpx.box[2],
|
||||||
|
glymur.jp2box.ContiguousCodestreamBox()]
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
jpx.wrap(tfile1.name, boxes=boxes)
|
||||||
|
|
||||||
|
def test_wrap_jpx_to_jp2_with_incorrect_jp2c_offset(self):
|
||||||
|
"""Reject A JPX file rewrapped with bad jp2c offset."""
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1:
|
||||||
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
jpch = jpx.box[5]
|
||||||
|
|
||||||
|
# The offset should be 902.
|
||||||
|
jpch.offset = 901
|
||||||
|
jpch.length = 313274
|
||||||
|
boxes = [jpx.box[0], jpx.box[1], jpx.box[2], jpch]
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
jpx.wrap(tfile1.name, boxes=boxes)
|
||||||
|
|
||||||
|
def test_wrap_jpx_to_jp2_with_correctly_specified_jp2c(self):
|
||||||
|
"""Accept A JPX file rewrapped with good jp2c."""
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1:
|
||||||
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
jpch = jpx.box[5]
|
||||||
|
|
||||||
|
# This time get it right.
|
||||||
|
jpch.offset = 903
|
||||||
|
jpch.length = 313274
|
||||||
|
boxes = [jpx.box[0], jpx.box[1], jpx.box[2], jpch]
|
||||||
|
jp2 = jpx.wrap(tfile1.name, boxes=boxes)
|
||||||
|
|
||||||
|
act_ids = [box.box_id for box in jp2.box]
|
||||||
|
exp_ids = ['jP ', 'ftyp', 'jp2h', 'jp2c']
|
||||||
|
self.assertEqual(act_ids, exp_ids)
|
||||||
|
|
||||||
|
act_offsets = [box.offset for box in jp2.box]
|
||||||
|
exp_offsets = [0, 12, 40, 887]
|
||||||
|
self.assertEqual(act_offsets, exp_offsets)
|
||||||
|
|
||||||
|
act_lengths = [box.length for box in jp2.box]
|
||||||
|
exp_lengths = [12, 28, 847, 313274]
|
||||||
|
self.assertEqual(act_lengths, exp_lengths)
|
||||||
|
|
||||||
|
def test_full_blown_jpx(self):
|
||||||
|
"""Rewrap a jpx file."""
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1:
|
||||||
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12]
|
||||||
|
boxes = [jpx.box[j] for j in idx]
|
||||||
|
jpx2 = jpx.wrap(tfile1.name, boxes=boxes)
|
||||||
|
exp_ids = [box.box_id for box in boxes]
|
||||||
|
lengths = [box.length for box in jpx.box]
|
||||||
|
exp_lengths = [lengths[j] for j in idx]
|
||||||
|
act_ids = [box.box_id for box in jpx2.box]
|
||||||
|
act_lengths = [box.length for box in jpx2.box]
|
||||||
|
self.assertEqual(exp_ids, act_ids)
|
||||||
|
self.assertEqual(exp_lengths, act_lengths)
|
||||||
|
|
||||||
|
|
||||||
class TestJp2Boxes(unittest.TestCase):
|
class TestJp2Boxes(unittest.TestCase):
|
||||||
"""Tests for canonical JP2 boxes."""
|
"""Tests for canonical JP2 boxes."""
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,22 @@ import struct
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
import glymur
|
import glymur
|
||||||
from glymur import Jp2k
|
from glymur import Jp2k
|
||||||
from glymur.jp2box import DataEntryURLBox, FileTypeBox, JPEG2000SignatureBox
|
from glymur.jp2box import DataEntryURLBox, FileTypeBox, JPEG2000SignatureBox
|
||||||
from glymur.jp2box import DataReferenceBox, FragmentListBox, FragmentTableBox
|
from glymur.jp2box import DataReferenceBox, FragmentListBox, FragmentTableBox
|
||||||
|
from glymur.jp2box import ColourSpecificationBox
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
class TestJPXWrap(unittest.TestCase):
|
class TestJPXWrap(unittest.TestCase):
|
||||||
"""Test suite for wrapping JPX files."""
|
"""Test suite for wrapping JPX files."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.jpxfile = glymur.data.jpxfile()
|
||||||
self.jp2file = glymur.data.nemo()
|
self.jp2file = glymur.data.nemo()
|
||||||
self.j2kfile = glymur.data.goodstuff()
|
self.j2kfile = glymur.data.goodstuff()
|
||||||
|
|
||||||
|
|
@ -107,6 +111,68 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
jp2.wrap(tfile.name, boxes=boxes)
|
jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
def test_jpch_jplh(self):
|
||||||
|
"""Write a codestream header, compositing layer header box."""
|
||||||
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
||||||
|
|
||||||
|
# The ftyp box must be modified to jpx.
|
||||||
|
boxes[1].brand = 'jpx '
|
||||||
|
boxes[1].compatibility_list = ['jp2 ', 'jpxb']
|
||||||
|
|
||||||
|
jpch = glymur.jp2box.CodestreamHeaderBox()
|
||||||
|
boxes.append(jpch)
|
||||||
|
jplh = glymur.jp2box.CompositingLayerHeaderBox()
|
||||||
|
boxes.append(jplh)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
|
||||||
|
jpx = jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
self.assertEqual(jpx.box[-2].box_id, 'jpch')
|
||||||
|
self.assertEqual(jpx.box[-1].box_id, 'jplh')
|
||||||
|
|
||||||
|
def test_cgrp(self):
|
||||||
|
"""Write a color group box."""
|
||||||
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
||||||
|
|
||||||
|
# The ftyp box must be modified to jpx.
|
||||||
|
boxes[1].brand = 'jpx '
|
||||||
|
boxes[1].compatibility_list = ['jp2 ', 'jpxb']
|
||||||
|
|
||||||
|
colr_rgb = ColourSpecificationBox(colorspace=glymur.core.SRGB)
|
||||||
|
colr_gr = ColourSpecificationBox(colorspace=glymur.core.GREYSCALE)
|
||||||
|
box = [colr_rgb, colr_gr]
|
||||||
|
|
||||||
|
cgrp = glymur.jp2box.ColourGroupBox(box=box)
|
||||||
|
boxes.append(cgrp)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
|
||||||
|
jpx = jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
self.assertEqual(jpx.box[-1].box_id, 'cgrp')
|
||||||
|
self.assertEqual(jpx.box[-1].box[0].box_id, 'colr')
|
||||||
|
self.assertEqual(jpx.box[-1].box[1].box_id, 'colr')
|
||||||
|
|
||||||
|
def test_cgrp_neg(self):
|
||||||
|
"""Can't write a cgrp with anything but colr sub boxes"""
|
||||||
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
||||||
|
|
||||||
|
# The ftyp box must be modified to jpx.
|
||||||
|
boxes[1].brand = 'jpx '
|
||||||
|
boxes[1].compatibility_list = ['jp2 ', 'jpxb']
|
||||||
|
|
||||||
|
lblb = glymur.jp2box.LabelBox("Just a test")
|
||||||
|
box = [lblb]
|
||||||
|
|
||||||
|
cgrp = glymur.jp2box.ColourGroupBox(box=box)
|
||||||
|
boxes.append(cgrp)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
jpx = jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
def test_ftbl(self):
|
def test_ftbl(self):
|
||||||
"""Write a fragment table box."""
|
"""Write a fragment table box."""
|
||||||
# Add a negative test where offset < 0
|
# Add a negative test where offset < 0
|
||||||
|
|
@ -207,13 +273,14 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
jp2.wrap(tfile.name, boxes=boxes)
|
jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||||
def test_deurl_child_of_dtbl(self):
|
def test_deurl_child_of_dtbl(self):
|
||||||
"""Data reference boxes can only contain data entry url boxes."""
|
"""Data reference boxes can only contain data entry url boxes."""
|
||||||
jp2 = Jp2k(self.jp2file)
|
jp2 = Jp2k(self.jp2file)
|
||||||
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
||||||
|
|
||||||
ftyp = glymur.jp2box.FileTypeBox()
|
ftyp = glymur.jp2box.FileTypeBox()
|
||||||
with self.assertRaises(IOError):
|
with self.assertWarns(UserWarning):
|
||||||
dref = glymur.jp2box.DataReferenceBox([ftyp])
|
dref = glymur.jp2box.DataReferenceBox([ftyp])
|
||||||
|
|
||||||
# Try to get around it by appending the ftyp box after creation.
|
# Try to get around it by appending the ftyp box after creation.
|
||||||
|
|
@ -337,7 +404,9 @@ class TestJPX(unittest.TestCase):
|
||||||
offset = [89]
|
offset = [89]
|
||||||
length = [1132288]
|
length = [1132288]
|
||||||
reference = [0, 0]
|
reference = [0, 0]
|
||||||
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
flst.write(tfile)
|
flst.write(tfile)
|
||||||
|
|
@ -347,8 +416,10 @@ class TestJPX(unittest.TestCase):
|
||||||
offset = [0]
|
offset = [0]
|
||||||
length = [1132288]
|
length = [1132288]
|
||||||
reference = [0]
|
reference = [0]
|
||||||
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
with warnings.catch_warnings():
|
||||||
with self.assertRaises(IOError):
|
warnings.simplefilter("ignore")
|
||||||
|
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
||||||
|
with self.assertRaises((IOError, OSError)):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
flst.write(tfile)
|
flst.write(tfile)
|
||||||
|
|
||||||
|
|
@ -357,14 +428,18 @@ class TestJPX(unittest.TestCase):
|
||||||
offset = [89]
|
offset = [89]
|
||||||
length = [0]
|
length = [0]
|
||||||
reference = [0]
|
reference = [0]
|
||||||
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
flst.write(tfile)
|
flst.write(tfile)
|
||||||
|
|
||||||
def test_ftbl_boxes_empty(self):
|
def test_ftbl_boxes_empty(self):
|
||||||
"""A fragment table box must have at least one child box."""
|
"""A fragment table box must have at least one child box."""
|
||||||
ftbl = glymur.jp2box.FragmentTableBox()
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
ftbl = glymur.jp2box.FragmentTableBox()
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
ftbl.write(tfile)
|
ftbl.write(tfile)
|
||||||
|
|
@ -377,6 +452,7 @@ class TestJPX(unittest.TestCase):
|
||||||
with tempfile.TemporaryFile() as tfile:
|
with tempfile.TemporaryFile() as tfile:
|
||||||
ftbl.write(tfile)
|
ftbl.write(tfile)
|
||||||
|
|
||||||
|
@unittest.skip("No such jpx file anymore.")
|
||||||
def test_jpx_rreq_mask_length_3(self):
|
def test_jpx_rreq_mask_length_3(self):
|
||||||
"""There are some JPX files with rreq mask length of 3."""
|
"""There are some JPX files with rreq mask length of 3."""
|
||||||
jpx = Jp2k(self.jpxfile)
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
|
@ -386,7 +462,7 @@ class TestJPX(unittest.TestCase):
|
||||||
self.assertEqual(jpx.box[2].standard_flag,
|
self.assertEqual(jpx.box[2].standard_flag,
|
||||||
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
|
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
@unittest.skip("Requires unnecessarily complicated code")
|
||||||
def test_unknown_superbox(self):
|
def test_unknown_superbox(self):
|
||||||
"""Verify that we can handle an unknown superbox."""
|
"""Verify that we can handle an unknown superbox."""
|
||||||
with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile:
|
||||||
|
|
@ -402,15 +478,9 @@ class TestJPX(unittest.TestCase):
|
||||||
|
|
||||||
with self.assertWarns(UserWarning):
|
with self.assertWarns(UserWarning):
|
||||||
jpx = Jp2k(tfile.name)
|
jpx = Jp2k(tfile.name)
|
||||||
self.assertEqual(jpx.box[-1].box_id, 'grp ')
|
self.assertEqual(jpx.box[-1].box_id, b'grp ')
|
||||||
self.assertEqual(jpx.box[-1].box[0].box_id, 'free')
|
self.assertEqual(jpx.box[-1].box[0].box_id, 'free')
|
||||||
|
|
||||||
def test_free_box(self):
|
|
||||||
"""Verify that we can handle a free box."""
|
|
||||||
j = Jp2k(self.jpxfile)
|
|
||||||
self.assertEqual(j.box[16].box[0].box_id, 'free')
|
|
||||||
self.assertEqual(type(j.box[16].box[0]), glymur.jp2box.FreeBox)
|
|
||||||
|
|
||||||
def test_data_reference_requires_dtbl(self):
|
def test_data_reference_requires_dtbl(self):
|
||||||
"""The existance of a data reference box requires a ftbl box as well."""
|
"""The existance of a data reference box requires a ftbl box as well."""
|
||||||
flag = 0
|
flag = 0
|
||||||
|
|
@ -483,17 +553,17 @@ class TestJPX(unittest.TestCase):
|
||||||
self.assertEqual(jpx.box[-1].box[0].data_reference, (3,))
|
self.assertEqual(jpx.box[-1].box[0].data_reference, (3,))
|
||||||
|
|
||||||
def test_nlst(self):
|
def test_nlst(self):
|
||||||
"""Verify that we can handle a free box."""
|
"""Verify that we can handle a number list box."""
|
||||||
j = Jp2k(self.jpxfile)
|
j = Jp2k(self.jpxfile)
|
||||||
self.assertEqual(j.box[16].box[1].box[0].box_id, 'nlst')
|
nlst = j.box[12].box[0].box[0]
|
||||||
self.assertEqual(type(j.box[16].box[1].box[0]),
|
self.assertEqual(nlst.box_id, 'nlst')
|
||||||
glymur.jp2box.NumberListBox)
|
self.assertEqual(type(nlst), glymur.jp2box.NumberListBox)
|
||||||
|
|
||||||
# Two associations.
|
# Two associations.
|
||||||
self.assertEqual(len(j.box[16].box[1].box[0].associations), 2)
|
self.assertEqual(len(nlst.associations), 2)
|
||||||
|
|
||||||
# Codestream 0
|
# Codestream 0
|
||||||
self.assertEqual(j.box[16].box[1].box[0].associations[0], 1 << 24)
|
self.assertEqual(nlst.associations[0], 1 << 24)
|
||||||
|
|
||||||
# Compositing Layer 0
|
# Compositing Layer 0
|
||||||
self.assertEqual(j.box[16].box[1].box[0].associations[1], 2 << 24)
|
self.assertEqual(nlst.associations[1], 2 << 24)
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ from glymur.jp2box import ColourSpecificationBox, ContiguousCodestreamBox
|
||||||
from glymur.jp2box import FileTypeBox, ImageHeaderBox, JP2HeaderBox
|
from glymur.jp2box import FileTypeBox, ImageHeaderBox, JP2HeaderBox
|
||||||
from glymur.jp2box import JPEG2000SignatureBox
|
from glymur.jp2box import JPEG2000SignatureBox
|
||||||
|
|
||||||
|
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
class TestXML(unittest.TestCase):
|
class TestXML(unittest.TestCase):
|
||||||
|
|
@ -286,3 +287,40 @@ class TestBadButRecoverableXmlFile(unittest.TestCase):
|
||||||
b'<test>this is a test</test>')
|
b'<test>this is a test</test>')
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
class TestXML_OpjDataRoot(unittest.TestCase):
|
||||||
|
"""Test suite for XML boxes, requires OPJ_DATA_ROOT."""
|
||||||
|
|
||||||
|
def test_bom(self):
|
||||||
|
"""Byte order markers are illegal in UTF-8. Issue 185"""
|
||||||
|
filename = opj_data_file(os.path.join('input',
|
||||||
|
'nonregression',
|
||||||
|
'issue171.jp2'))
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
self.assertIsNotNone(jp2.box[3].xml)
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_utf8(self):
|
||||||
|
"""Bad byte sequence that cannot be parsed."""
|
||||||
|
filename = opj_data_file(os.path.join('input',
|
||||||
|
'nonregression',
|
||||||
|
'26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'))
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
self.assertIsNone(jp2.box[3].box[1].box[1].xml)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ if HAS_PYTHON_XMP_TOOLKIT:
|
||||||
from libxmp import XMPMeta
|
from libxmp import XMPMeta
|
||||||
|
|
||||||
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||||
|
from . import fixtures
|
||||||
|
|
||||||
# Doc tests should be run as well.
|
# Doc tests should be run as well.
|
||||||
def load_tests(loader, tests, ignore):
|
def load_tests(loader, tests, ignore):
|
||||||
|
|
@ -53,9 +53,7 @@ def load_tests(loader, tests, ignore):
|
||||||
|
|
||||||
|
|
||||||
class TestJp2k(unittest.TestCase):
|
class TestJp2k(unittest.TestCase):
|
||||||
"""Test suite for openjpeg software starting at 1.3"""
|
"""These tests should be run by just about all configuration."""
|
||||||
|
|
||||||
# These tests should be run by just about all configuration.
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.jp2file = glymur.data.nemo()
|
self.jp2file = glymur.data.nemo()
|
||||||
|
|
@ -65,6 +63,24 @@ class TestJp2k(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_no_cxform_pclr_jpx(self):
|
||||||
|
"""Indices for pclr jpxfile if no color transform"""
|
||||||
|
j = Jp2k(self.jpxfile)
|
||||||
|
rgb = j.read()
|
||||||
|
idx = j.read(ignore_pclr_cmap_cdef=True)
|
||||||
|
nr, nc = 1024, 1024
|
||||||
|
self.assertEqual(rgb.shape, (nr, nc, 3))
|
||||||
|
self.assertEqual(idx.shape, (nr, nc))
|
||||||
|
|
||||||
|
# Should be able to manually reconstruct the RGB image from the palette
|
||||||
|
# and indices.
|
||||||
|
palette = j.box[2].box[2].palette
|
||||||
|
rgb_from_idx = np.zeros(rgb.shape, dtype=np.uint8)
|
||||||
|
for r in np.arange(nr):
|
||||||
|
for c in np.arange(nc):
|
||||||
|
rgb_from_idx[r, c] = palette[idx[r, c]]
|
||||||
|
np.testing.assert_array_equal(rgb, rgb_from_idx)
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
"""Verify that results of __repr__ are eval-able."""
|
"""Verify that results of __repr__ are eval-able."""
|
||||||
j = Jp2k(self.j2kfile)
|
j = Jp2k(self.j2kfile)
|
||||||
|
|
@ -247,19 +263,6 @@ class TestJp2k(unittest.TestCase):
|
||||||
j2k = Jp2k(self.j2kfile)
|
j2k = Jp2k(self.j2kfile)
|
||||||
j2k.read()
|
j2k.read()
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_read_differing_subsamples(self):
|
|
||||||
"""should error out with read used on differently subsampled images"""
|
|
||||||
# Verify that we error out appropriately if we use the read method
|
|
||||||
# on an image with differing subsamples
|
|
||||||
#
|
|
||||||
# Issue 86.
|
|
||||||
filename = opj_data_file('input/conformance/p0_05.j2k')
|
|
||||||
j = Jp2k(filename)
|
|
||||||
with self.assertRaises(RuntimeError):
|
|
||||||
j.read()
|
|
||||||
|
|
||||||
def test_empty_box_with_j2k(self):
|
def test_empty_box_with_j2k(self):
|
||||||
"""Verify that the list of boxes in a J2C/J2K file is present, but
|
"""Verify that the list of boxes in a J2C/J2K file is present, but
|
||||||
empty.
|
empty.
|
||||||
|
|
@ -376,20 +379,18 @@ 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(fixtures.OPENJP2_IS_V2_OFFICIAL,
|
||||||
|
"Feature not supported in 2.0.0 official")
|
||||||
|
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
|
||||||
|
"Feature not supported in 1.5")
|
||||||
def test_jpx_mult_codestreams_jp2_brand(self):
|
def test_jpx_mult_codestreams_jp2_brand(self):
|
||||||
"""Read JPX codestream when jp2-compatible."""
|
"""Read JPX codestream when jp2-compatible."""
|
||||||
# The file in question has multiple codestreams.
|
# The file in question has multiple codestreams.
|
||||||
jpx = Jp2k(self.jpxfile)
|
jpx = Jp2k(self.jpxfile)
|
||||||
data = jpx.read()
|
data = jpx.read()
|
||||||
if re.match(r"""1\.[0123]""", glymur.version.openjpeg_version):
|
self.assertEqual(data.shape, (1024, 1024, 3))
|
||||||
# openjpeg 1.3 doesn't apply the palette, so it's a 2D image here
|
|
||||||
self.assertEqual(data.shape, (1024, 1024))
|
|
||||||
else:
|
|
||||||
self.assertEqual(data.shape, (1024, 1024, 3))
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version),
|
|
||||||
"Requires at least version 1.5")
|
|
||||||
class TestJp2k_write(unittest.TestCase):
|
class TestJp2k_write(unittest.TestCase):
|
||||||
"""Write tests, can be run by versions 1.5+"""
|
"""Write tests, can be run by versions 1.5+"""
|
||||||
|
|
||||||
|
|
@ -753,6 +754,106 @@ class TestJp2k_2_1(unittest.TestCase):
|
||||||
with self.assertRaisesRegex((IOError, OSError), regexp):
|
with self.assertRaisesRegex((IOError, OSError), regexp):
|
||||||
j.read(rlevel=1)
|
j.read(rlevel=1)
|
||||||
|
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
class TestJp2kOpjDataRoot(unittest.TestCase):
|
||||||
|
"""These tests should be run by just about all configuration."""
|
||||||
|
|
||||||
|
def test_undecodeable_box_id(self):
|
||||||
|
"""Should warn in case of undecodeable box ID but not error out."""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1013627.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
# Now make sure we got all of the boxes. Ignore the last, which was
|
||||||
|
# bad.
|
||||||
|
box_ids = [box.box_id for box in jp2.box[:-1]]
|
||||||
|
self.assertEqual(box_ids, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
|
||||||
|
|
||||||
|
def test_invalid_approximation(self):
|
||||||
|
"""Should warn in case of bad ftyp brand."""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1000290.jp2')
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+")
|
||||||
|
def test_invalid_approximation(self):
|
||||||
|
"""Should warn in case of invalid approximation."""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1015644.jp2')
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+")
|
||||||
|
def test_invalid_colorspace(self):
|
||||||
|
"""Should warn in case of invalid colorspace."""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2')
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
def test_no_cxform_pclr_jp2(self):
|
||||||
|
"""Indices for pclr jpxfile if no color transform"""
|
||||||
|
filename = opj_data_file('input/conformance/file9.jp2')
|
||||||
|
j = Jp2k(filename)
|
||||||
|
rgb = j.read()
|
||||||
|
idx = j.read(ignore_pclr_cmap_cdef=True)
|
||||||
|
self.assertEqual(rgb.shape, (512, 768, 3))
|
||||||
|
self.assertEqual(idx.shape, (512, 768))
|
||||||
|
|
||||||
|
# Should be able to manually reconstruct the RGB image from the palette
|
||||||
|
# and indices.
|
||||||
|
palette = j.box[2].box[1].palette
|
||||||
|
rgb_from_idx = np.zeros(rgb.shape, dtype=np.uint8)
|
||||||
|
for r in np.arange(rgb.shape[0]):
|
||||||
|
for c in np.arange(rgb.shape[1]):
|
||||||
|
rgb_from_idx[r, c] = palette[idx[r, c]]
|
||||||
|
np.testing.assert_array_equal(rgb, rgb_from_idx)
|
||||||
|
|
||||||
|
def test_stupid_windows_eol_at_end(self):
|
||||||
|
"""Garbage characters at the end of the file."""
|
||||||
|
filename = opj_data_file('input/nonregression/issue211.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
def test_read_differing_subsamples(self):
|
||||||
|
"""should error out with read used on differently subsampled images"""
|
||||||
|
# Verify that we error out appropriately if we use the read method
|
||||||
|
# on an image with differing subsamples
|
||||||
|
#
|
||||||
|
# Issue 86.
|
||||||
|
filename = opj_data_file('input/conformance/p0_05.j2k')
|
||||||
|
j = Jp2k(filename)
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
j.read()
|
||||||
|
|
||||||
|
def test_no_cxform_cmap(self):
|
||||||
|
"""Bands as physically ordered, not as physically intended"""
|
||||||
|
# This file has the components physically reversed. The cmap box
|
||||||
|
# tells the decoder how to order them, but this flag prevents that.
|
||||||
|
filename = opj_data_file('input/conformance/file2.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# The file has a bad compatibility list entry. Not important here.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = Jp2k(filename)
|
||||||
|
ycbcr = j.read()
|
||||||
|
crcby = j.read(ignore_pclr_cmap_cdef=True)
|
||||||
|
|
||||||
|
expected = np.zeros(ycbcr.shape, ycbcr.dtype)
|
||||||
|
for k in range(crcby.shape[2]):
|
||||||
|
expected[:,:,crcby.shape[2] - k - 1] = crcby[:,:,k]
|
||||||
|
|
||||||
|
np.testing.assert_array_equal(ycbcr, expected)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,10 @@ class TestSuite(unittest.TestCase):
|
||||||
|
|
||||||
def test_ETS_JP2_file1(self):
|
def test_ETS_JP2_file1(self):
|
||||||
jfile = opj_data_file('input/conformance/file1.jp2')
|
jfile = opj_data_file('input/conformance/file1.jp2')
|
||||||
jp2k = Jp2k(jfile)
|
with warnings.catch_warnings():
|
||||||
|
# Bad compatibility list item.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2k = Jp2k(jfile)
|
||||||
jpdata = jp2k.read()
|
jpdata = jp2k.read()
|
||||||
self.assertEqual(jpdata.shape, (512, 768, 3))
|
self.assertEqual(jpdata.shape, (512, 768, 3))
|
||||||
|
|
||||||
|
|
@ -381,11 +384,7 @@ class TestSuite(unittest.TestCase):
|
||||||
jfile = opj_data_file('input/conformance/file9.jp2')
|
jfile = opj_data_file('input/conformance/file9.jp2')
|
||||||
jp2k = Jp2k(jfile)
|
jp2k = Jp2k(jfile)
|
||||||
jpdata = jp2k.read()
|
jpdata = jp2k.read()
|
||||||
if re.match(r"""1\.3""", glymur.version.openjpeg_version):
|
self.assertEqual(jpdata.shape, (512, 768, 3))
|
||||||
# Version 1.3 reads the indexed image as indices, not as RGB.
|
|
||||||
self.assertEqual(jpdata.shape, (512, 768))
|
|
||||||
else:
|
|
||||||
self.assertEqual(jpdata.shape, (512, 768, 3))
|
|
||||||
|
|
||||||
def test_NR_DEC_Bretagne2_j2k_1_decode(self):
|
def test_NR_DEC_Bretagne2_j2k_1_decode(self):
|
||||||
jfile = opj_data_file('input/nonregression/Bretagne2.j2k')
|
jfile = opj_data_file('input/nonregression/Bretagne2.j2k')
|
||||||
|
|
@ -465,7 +464,9 @@ class TestSuite(unittest.TestCase):
|
||||||
def test_NR_DEC_illegalcolortransform_j2k_14_decode(self):
|
def test_NR_DEC_illegalcolortransform_j2k_14_decode(self):
|
||||||
# Stream too short, expected SOT.
|
# Stream too short, expected SOT.
|
||||||
jfile = opj_data_file('input/nonregression/illegalcolortransform.j2k')
|
jfile = opj_data_file('input/nonregression/illegalcolortransform.j2k')
|
||||||
Jp2k(jfile).read()
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
Jp2k(jfile).read()
|
||||||
self.assertTrue(True)
|
self.assertTrue(True)
|
||||||
|
|
||||||
def test_NR_DEC_j2k32_j2k_15_decode(self):
|
def test_NR_DEC_j2k32_j2k_15_decode(self):
|
||||||
|
|
@ -3116,7 +3117,10 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
|
|
||||||
def test_NR_file1_dump(self):
|
def test_NR_file1_dump(self):
|
||||||
jfile = opj_data_file('input/conformance/file1.jp2')
|
jfile = opj_data_file('input/conformance/file1.jp2')
|
||||||
jp2 = Jp2k(jfile)
|
with warnings.catch_warnings():
|
||||||
|
# Bad compatibility list item.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(jfile)
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box]
|
ids = [box.box_id for box in jp2.box]
|
||||||
self.assertEqual(ids, ['jP ', 'ftyp', 'xml ', 'jp2h', 'xml ',
|
self.assertEqual(ids, ['jP ', 'ftyp', 'xml ', 'jp2h', 'xml ',
|
||||||
|
|
@ -3297,7 +3301,7 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
|
|
||||||
def test_NR_file5_dump(self):
|
def test_NR_file5_dump(self):
|
||||||
# Three 8-bit components in the ROMM-RGB colourspace, encapsulated in a
|
# Three 8-bit components in the ROMM-RGB colourspace, encapsulated in a
|
||||||
# JP2 compatible JPX file. The components have been transformed using
|
# JPX file. The components have been transformed using
|
||||||
# the RCT. The colourspace is specified using both a Restricted ICC
|
# the RCT. The colourspace is specified using both a Restricted ICC
|
||||||
# profile and using the JPX-defined enumerated code for the ROMM-RGB
|
# profile and using the JPX-defined enumerated code for the ROMM-RGB
|
||||||
# colourspace.
|
# colourspace.
|
||||||
|
|
@ -3305,49 +3309,37 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
jp2 = Jp2k(jfile)
|
jp2 = Jp2k(jfile)
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box]
|
ids = [box.box_id for box in jp2.box]
|
||||||
self.assertEqual(ids, ['jP ', 'ftyp', 'rreq', 'jp2h', 'jp2c'])
|
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box[3].box]
|
ids = [box.box_id for box in jp2.box[2].box]
|
||||||
self.assertEqual(ids, ['ihdr', 'colr', 'colr'])
|
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||||
|
|
||||||
# Signature box. Check for corruption.
|
# Signature box. Check for corruption.
|
||||||
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
||||||
|
|
||||||
# File type box.
|
# File type box.
|
||||||
self.assertEqual(jp2.box[1].brand, 'jpx ')
|
self.assertEqual(jp2.box[1].brand, 'jp2 ')
|
||||||
self.assertEqual(jp2.box[1].minor_version, 0)
|
self.assertEqual(jp2.box[1].minor_version, 0)
|
||||||
self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ')
|
self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ')
|
||||||
self.assertEqual(jp2.box[1].compatibility_list[2], 'jpx ')
|
|
||||||
self.assertEqual(jp2.box[1].compatibility_list[3], 'jpxb')
|
|
||||||
|
|
||||||
# Jp2 Header
|
# Jp2 Header
|
||||||
# Image header
|
# Image header
|
||||||
self.assertEqual(jp2.box[3].box[0].height, 512)
|
self.assertEqual(jp2.box[2].box[0].height, 512)
|
||||||
self.assertEqual(jp2.box[3].box[0].width, 768)
|
self.assertEqual(jp2.box[2].box[0].width, 768)
|
||||||
self.assertEqual(jp2.box[3].box[0].num_components, 3)
|
self.assertEqual(jp2.box[2].box[0].num_components, 3)
|
||||||
self.assertEqual(jp2.box[3].box[0].signed, False)
|
self.assertEqual(jp2.box[2].box[0].signed, False)
|
||||||
self.assertEqual(jp2.box[3].box[0].compression, 7) # wavelet
|
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||||
self.assertEqual(jp2.box[3].box[0].colorspace_unknown, False)
|
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
|
||||||
self.assertEqual(jp2.box[3].box[0].ip_provided, False)
|
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
|
||||||
|
|
||||||
# Jp2 Header
|
# Jp2 Header
|
||||||
# Colour specification
|
# Colour specification
|
||||||
self.assertEqual(jp2.box[3].box[1].method,
|
self.assertEqual(jp2.box[2].box[1].method,
|
||||||
glymur.core.RESTRICTED_ICC_PROFILE) # enumerated
|
glymur.core.RESTRICTED_ICC_PROFILE) # enumerated
|
||||||
self.assertEqual(jp2.box[3].box[1].precedence, 0)
|
self.assertEqual(jp2.box[2].box[1].precedence, 0)
|
||||||
self.assertEqual(jp2.box[3].box[1].approximation, 1) # JPX exact
|
self.assertEqual(jp2.box[2].box[1].approximation, 1) # JPX exact
|
||||||
self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 546)
|
self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 546)
|
||||||
self.assertIsNone(jp2.box[3].box[1].colorspace)
|
self.assertIsNone(jp2.box[2].box[1].colorspace)
|
||||||
|
|
||||||
# Jp2 Header
|
|
||||||
# Colour specification
|
|
||||||
self.assertEqual(jp2.box[3].box[2].method,
|
|
||||||
glymur.core.ENUMERATED_COLORSPACE)
|
|
||||||
self.assertEqual(jp2.box[3].box[2].precedence, 1)
|
|
||||||
self.assertEqual(jp2.box[3].box[2].approximation, 1) # JPX exact
|
|
||||||
self.assertIsNone(jp2.box[3].box[2].icc_profile)
|
|
||||||
self.assertEqual(jp2.box[3].box[2].colorspace,
|
|
||||||
glymur.core.ROMM_RGB)
|
|
||||||
|
|
||||||
def test_NR_file6_dump(self):
|
def test_NR_file6_dump(self):
|
||||||
jfile = opj_data_file('input/conformance/file6.jp2')
|
jfile = opj_data_file('input/conformance/file6.jp2')
|
||||||
|
|
@ -3398,54 +3390,37 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
jp2 = Jp2k(jfile)
|
jp2 = Jp2k(jfile)
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box]
|
ids = [box.box_id for box in jp2.box]
|
||||||
self.assertEqual(ids, ['jP ', 'ftyp', 'rreq', 'jp2h', 'jp2c'])
|
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box[3].box]
|
ids = [box.box_id for box in jp2.box[2].box]
|
||||||
self.assertEqual(ids, ['ihdr', 'colr', 'colr'])
|
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||||
|
|
||||||
# Signature box. Check for corruption.
|
# Signature box. Check for corruption.
|
||||||
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
||||||
|
|
||||||
# File type box.
|
# File type box.
|
||||||
self.assertEqual(jp2.box[1].brand, 'jpx ')
|
self.assertEqual(jp2.box[1].brand, 'jp2 ')
|
||||||
self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ')
|
self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ')
|
||||||
self.assertEqual(jp2.box[1].compatibility_list[2], 'jpx ')
|
|
||||||
self.assertEqual(jp2.box[1].compatibility_list[3], 'jpxb')
|
|
||||||
self.assertEqual(jp2.box[1].minor_version, 0)
|
|
||||||
|
|
||||||
# Reader requirements talk.
|
|
||||||
# e-SRGB enumerated colourspace
|
|
||||||
self.assertTrue(60 in jp2.box[2].standard_flag)
|
|
||||||
|
|
||||||
# Jp2 Header
|
# Jp2 Header
|
||||||
# Image header
|
# Image header
|
||||||
self.assertEqual(jp2.box[3].box[0].height, 640)
|
self.assertEqual(jp2.box[2].box[0].height, 640)
|
||||||
self.assertEqual(jp2.box[3].box[0].width, 480)
|
self.assertEqual(jp2.box[2].box[0].width, 480)
|
||||||
self.assertEqual(jp2.box[3].box[0].num_components, 3)
|
self.assertEqual(jp2.box[2].box[0].num_components, 3)
|
||||||
self.assertEqual(jp2.box[3].box[0].bits_per_component, 16)
|
self.assertEqual(jp2.box[2].box[0].bits_per_component, 16)
|
||||||
self.assertEqual(jp2.box[3].box[0].signed, False)
|
self.assertEqual(jp2.box[2].box[0].signed, False)
|
||||||
self.assertEqual(jp2.box[3].box[0].compression, 7) # wavelet
|
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||||
self.assertEqual(jp2.box[3].box[0].colorspace_unknown, False)
|
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
|
||||||
self.assertEqual(jp2.box[3].box[0].ip_provided, False)
|
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
|
||||||
|
|
||||||
# Jp2 Header
|
# Jp2 Header
|
||||||
# Colour specification
|
# Colour specification
|
||||||
self.assertEqual(jp2.box[3].box[1].method,
|
self.assertEqual(jp2.box[2].box[1].method,
|
||||||
glymur.core.RESTRICTED_ICC_PROFILE)
|
glymur.core.RESTRICTED_ICC_PROFILE)
|
||||||
self.assertEqual(jp2.box[3].box[1].precedence, 0)
|
self.assertEqual(jp2.box[2].box[1].precedence, 0)
|
||||||
self.assertEqual(jp2.box[3].box[1].approximation, 1) # JPX exact
|
self.assertEqual(jp2.box[2].box[1].approximation, 1)
|
||||||
self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 13332)
|
self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 13332)
|
||||||
self.assertIsNone(jp2.box[3].box[1].colorspace)
|
self.assertIsNone(jp2.box[2].box[1].colorspace)
|
||||||
|
|
||||||
# Jp2 Header
|
|
||||||
# Colour specification
|
|
||||||
self.assertEqual(jp2.box[3].box[2].method,
|
|
||||||
glymur.core.ENUMERATED_COLORSPACE)
|
|
||||||
self.assertEqual(jp2.box[3].box[2].precedence, 1)
|
|
||||||
self.assertEqual(jp2.box[3].box[2].approximation, 1) # JPX exact
|
|
||||||
self.assertIsNone(jp2.box[3].box[2].icc_profile)
|
|
||||||
self.assertEqual(jp2.box[3].box[2].colorspace,
|
|
||||||
glymur.core.E_SRGB)
|
|
||||||
|
|
||||||
def test_NR_file8_dump(self):
|
def test_NR_file8_dump(self):
|
||||||
# One 8-bit component in a gamma 1.8 space. The colourspace is
|
# One 8-bit component in a gamma 1.8 space. The colourspace is
|
||||||
|
|
@ -3472,7 +3447,7 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
# Image header
|
# Image header
|
||||||
self.assertEqual(jp2.box[2].box[0].height, 400)
|
self.assertEqual(jp2.box[2].box[0].height, 400)
|
||||||
self.assertEqual(jp2.box[2].box[0].width, 700)
|
self.assertEqual(jp2.box[2].box[0].width, 700)
|
||||||
self.assertEqual(jp2.box[2].box[0].num_components, 1)
|
self.assertEqual(jp2.box[2].box[0].num_components, 3)
|
||||||
self.assertEqual(jp2.box[2].box[0].bits_per_component, 8)
|
self.assertEqual(jp2.box[2].box[0].bits_per_component, 8)
|
||||||
self.assertEqual(jp2.box[2].box[0].signed, False)
|
self.assertEqual(jp2.box[2].box[0].signed, False)
|
||||||
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||||
|
|
@ -5507,7 +5482,7 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
jp2 = Jp2k(jfile)
|
jp2 = Jp2k(jfile)
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box]
|
ids = [box.box_id for box in jp2.box]
|
||||||
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'XML ', 'jp2c'])
|
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', b'XML ', 'jp2c'])
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box[2].box]
|
ids = [box.box_id for box in jp2.box[2].box]
|
||||||
self.assertEqual(ids, ['ihdr', 'colr'])
|
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||||
|
|
@ -5861,9 +5836,9 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
|
|
||||||
# Jp2 Header
|
# Jp2 Header
|
||||||
# Component mapping box
|
# Component mapping box
|
||||||
self.assertEqual(jp2.box[3].box[3].component_index, (0, 1, 2))
|
self.assertEqual(jp2.box[3].box[3].component_index, (0, 0, 0, 0))
|
||||||
self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 0))
|
self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1, 1))
|
||||||
self.assertEqual(jp2.box[3].box[3].palette_index, (0, 0, 1))
|
self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2, 3))
|
||||||
|
|
||||||
c = jp2.box[4].main_header
|
c = jp2.box[4].main_header
|
||||||
|
|
||||||
|
|
@ -6336,11 +6311,16 @@ class TestSuiteDump(unittest.TestCase):
|
||||||
[8, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10])
|
[8, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10])
|
||||||
|
|
||||||
def test_NR_text_GBR_dump(self):
|
def test_NR_text_GBR_dump(self):
|
||||||
|
# brand is 'jp2 ', but has any icc profile.
|
||||||
|
# Verify the warning on python3, but ignore it otherwise.
|
||||||
jfile = opj_data_file('input/nonregression/text_GBR.jp2')
|
jfile = opj_data_file('input/nonregression/text_GBR.jp2')
|
||||||
with warnings.catch_warnings():
|
if sys.hexversion > 0x03030000:
|
||||||
# brand is 'jp2 ', but has any icc profile.
|
with self.assertWarns(UserWarning):
|
||||||
warnings.simplefilter("ignore")
|
jp2 = Jp2k(jfile)
|
||||||
jp2 = Jp2k(jfile)
|
else:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(jfile)
|
||||||
|
|
||||||
ids = [box.box_id for box in jp2.box]
|
ids = [box.box_id for box in jp2.box]
|
||||||
lst = ['jP ', 'ftyp', 'rreq', 'jp2h',
|
lst = ['jP ', 'ftyp', 'rreq', 'jp2h',
|
||||||
|
|
@ -6647,6 +6627,7 @@ class TestSuite2point1(unittest.TestCase):
|
||||||
Jp2k(jfile).read()
|
Jp2k(jfile).read()
|
||||||
self.assertTrue(True)
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@unittest.skip("Failing as of r2436")
|
||||||
def test_NR_DEC_mem_b2ace68c_1381_jp2_34_decode(self):
|
def test_NR_DEC_mem_b2ace68c_1381_jp2_34_decode(self):
|
||||||
jfile = opj_data_file('input/nonregression/mem-b2ace68c-1381.jp2')
|
jfile = opj_data_file('input/nonregression/mem-b2ace68c-1381.jp2')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@ from glymur import Jp2k
|
||||||
import glymur
|
import glymur
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version),
|
|
||||||
"Functionality not implemented for 1.3, 1.4")
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_OPJ_DATA_ROOT environment variable not set")
|
"OPJ_OPJ_DATA_ROOT environment variable not set")
|
||||||
class TestSuiteNegative(unittest.TestCase):
|
class TestSuiteNegative(unittest.TestCase):
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,252 @@ import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
try:
|
||||||
|
import skimage.io
|
||||||
|
skimage.io.use_plugin('freeimage', 'imread')
|
||||||
|
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = True
|
||||||
|
except ((ImportError, RuntimeError)):
|
||||||
|
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = False
|
||||||
|
|
||||||
from .fixtures import read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
from .fixtures import read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
||||||
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||||
|
from . import fixtures
|
||||||
|
|
||||||
from glymur import Jp2k
|
from glymur import Jp2k
|
||||||
import glymur
|
import glymur
|
||||||
|
|
||||||
|
@unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT,
|
||||||
|
"Cannot read input image without scikit-image/freeimage")
|
||||||
|
@unittest.skipIf(os.name == "nt", "no write support on windows, period")
|
||||||
|
@unittest.skipIf(fixtures.OPENJP2_IS_V2_OFFICIAL,
|
||||||
|
"Feature not supported in 2.0.0 official")
|
||||||
|
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
|
||||||
|
"Feature not supported in 1.5")
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
class TestSuiteWriteCinema(unittest.TestCase):
|
||||||
|
"""Tests for writing with openjp2 backend.
|
||||||
|
|
||||||
|
These tests either roughly correspond with those tests with similar names
|
||||||
|
in the OpenJPEG test suite or are closely associated.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_cinema2K_with_others(self):
|
||||||
|
"""Can't specify cinema2k with any other options."""
|
||||||
|
relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
j.write(data, cinema2k=48, cratios=[200, 100, 50])
|
||||||
|
|
||||||
|
def test_cinema4K_with_others(self):
|
||||||
|
"""Can't specify cinema4k with any other options."""
|
||||||
|
relfile = 'input/nonregression/ElephantDream_4K.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
j.write(data, cinema4k=True, cratios=[200, 100, 50])
|
||||||
|
|
||||||
|
def check_cinema4k_codestream(self, codestream, image_size):
|
||||||
|
"""Common out for cinema2k tests."""
|
||||||
|
# SIZ: Image and tile size
|
||||||
|
# Profile: "3" means cinema2K
|
||||||
|
self.assertEqual(codestream.segment[1].rsiz, 4)
|
||||||
|
# Reference grid size
|
||||||
|
self.assertEqual((codestream.segment[1].xsiz,
|
||||||
|
codestream.segment[1].ysiz),
|
||||||
|
image_size)
|
||||||
|
# Reference grid offset
|
||||||
|
self.assertEqual((codestream.segment[1].xosiz,
|
||||||
|
codestream.segment[1].yosiz), (0, 0))
|
||||||
|
# Tile size
|
||||||
|
self.assertEqual((codestream.segment[1].xtsiz,
|
||||||
|
codestream.segment[1].ytsiz),
|
||||||
|
image_size)
|
||||||
|
# Tile offset
|
||||||
|
self.assertEqual((codestream.segment[1].xtosiz,
|
||||||
|
codestream.segment[1].ytosiz),
|
||||||
|
(0, 0))
|
||||||
|
# bitdepth
|
||||||
|
self.assertEqual(codestream.segment[1].bitdepth, (12, 12, 12))
|
||||||
|
# signed
|
||||||
|
self.assertEqual(codestream.segment[1].signed,
|
||||||
|
(False, False, False))
|
||||||
|
# subsampling
|
||||||
|
self.assertEqual(list(zip(codestream.segment[1].xrsiz,
|
||||||
|
codestream.segment[1].yrsiz)),
|
||||||
|
[(1, 1)] * 3)
|
||||||
|
|
||||||
|
# COD: Coding style default
|
||||||
|
self.assertFalse(codestream.segment[2].scod & 2) # no sop
|
||||||
|
self.assertFalse(codestream.segment[2].scod & 4) # no eph
|
||||||
|
self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CPRL)
|
||||||
|
self.assertEqual(codestream.segment[2].layers, 1)
|
||||||
|
self.assertEqual(codestream.segment[2].spcod[3], 1) # mct
|
||||||
|
self.assertEqual(codestream.segment[2].spcod[4], 5) # levels
|
||||||
|
self.assertEqual(tuple(codestream.segment[2].code_block_size),
|
||||||
|
(32, 32)) # cblksz
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def check_cinema2k_codestream(self, codestream, image_size):
|
||||||
|
"""Common out for cinema2k tests."""
|
||||||
|
# SIZ: Image and tile size
|
||||||
|
# Profile: "3" means cinema2K
|
||||||
|
self.assertEqual(codestream.segment[1].rsiz, 3)
|
||||||
|
# Reference grid size
|
||||||
|
self.assertEqual((codestream.segment[1].xsiz,
|
||||||
|
codestream.segment[1].ysiz),
|
||||||
|
image_size)
|
||||||
|
# Reference grid offset
|
||||||
|
self.assertEqual((codestream.segment[1].xosiz,
|
||||||
|
codestream.segment[1].yosiz), (0, 0))
|
||||||
|
# Tile size
|
||||||
|
self.assertEqual((codestream.segment[1].xtsiz,
|
||||||
|
codestream.segment[1].ytsiz),
|
||||||
|
image_size)
|
||||||
|
# Tile offset
|
||||||
|
self.assertEqual((codestream.segment[1].xtosiz,
|
||||||
|
codestream.segment[1].ytosiz),
|
||||||
|
(0, 0))
|
||||||
|
# bitdepth
|
||||||
|
self.assertEqual(codestream.segment[1].bitdepth, (12, 12, 12))
|
||||||
|
# signed
|
||||||
|
self.assertEqual(codestream.segment[1].signed,
|
||||||
|
(False, False, False))
|
||||||
|
# subsampling
|
||||||
|
self.assertEqual(list(zip(codestream.segment[1].xrsiz,
|
||||||
|
codestream.segment[1].yrsiz)),
|
||||||
|
[(1, 1)] * 3)
|
||||||
|
|
||||||
|
# COD: Coding style default
|
||||||
|
self.assertFalse(codestream.segment[2].scod & 2) # no sop
|
||||||
|
self.assertFalse(codestream.segment[2].scod & 4) # no eph
|
||||||
|
self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CPRL)
|
||||||
|
self.assertEqual(codestream.segment[2].layers, 1)
|
||||||
|
self.assertEqual(codestream.segment[2].spcod[3], 1) # mct
|
||||||
|
self.assertEqual(codestream.segment[2].spcod[4], 5) # levels
|
||||||
|
self.assertEqual(tuple(codestream.segment[2].code_block_size),
|
||||||
|
(32, 32)) # cblksz
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_NR_ENC_ElephantDream_4K_tif_21_encode(self):
|
||||||
|
relfile = 'input/nonregression/ElephantDream_4K.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
j.write(data, cinema4k=True)
|
||||||
|
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
self.check_cinema4k_codestream(codestream, (4096, 2160))
|
||||||
|
|
||||||
|
|
||||||
|
def test_NR_ENC_X_5_2K_24_235_CBR_STEM24_000_tif_19_encode(self):
|
||||||
|
relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
j.write(data, cinema2k=48)
|
||||||
|
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
self.check_cinema2k_codestream(codestream, (2048, 857))
|
||||||
|
|
||||||
|
|
||||||
|
def test_NR_ENC_X_6_2K_24_FULL_CBR_CIRCLE_000_tif_20_encode(self):
|
||||||
|
relfile = 'input/nonregression/X_6_2K_24_FULL_CBR_CIRCLE_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
j.write(data, cinema2k=48)
|
||||||
|
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
self.check_cinema2k_codestream(codestream, (2048, 1080))
|
||||||
|
|
||||||
|
|
||||||
|
def test_NR_ENC_X_6_2K_24_FULL_CBR_CIRCLE_000_tif_17_encode(self):
|
||||||
|
relfile = 'input/nonregression/X_6_2K_24_FULL_CBR_CIRCLE_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
j.write(data, cinema2k=24)
|
||||||
|
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
self.check_cinema2k_codestream(codestream, (2048, 1080))
|
||||||
|
|
||||||
|
|
||||||
|
def test_NR_ENC_X_5_2K_24_235_CBR_STEM24_000_tif_16_encode(self):
|
||||||
|
relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
j.write(data, cinema2k=24)
|
||||||
|
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
self.check_cinema2k_codestream(codestream, (2048, 857))
|
||||||
|
|
||||||
|
|
||||||
|
def test_NR_ENC_X_4_2K_24_185_CBR_WB_000_tif_18_encode(self):
|
||||||
|
relfile = 'input/nonregression/X_4_2K_24_185_CBR_WB_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
j.write(data, cinema2k=48)
|
||||||
|
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
self.check_cinema2k_codestream(codestream, (1998, 1080))
|
||||||
|
|
||||||
|
@unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT,
|
||||||
|
"Cannot read input image without scikit-image/freeimage")
|
||||||
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
|
@unittest.skipIf(not re.match("(1.5|2.0)", glymur.version.openjpeg_version),
|
||||||
|
"Functionality implemented for 2.1")
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_OPJ_DATA_ROOT environment variable not set")
|
||||||
|
class TestSuiteNegative2pointzero(unittest.TestCase):
|
||||||
|
"""Feature set not supported for versions less than 2.0"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.jp2file = glymur.data.nemo()
|
||||||
|
self.j2kfile = glymur.data.goodstuff()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_cinema_mode(self):
|
||||||
|
relfile = 'input/nonregression/X_4_2K_24_185_CBR_WB_000.tif'
|
||||||
|
infile = opj_data_file(relfile)
|
||||||
|
data = skimage.io.imread(infile)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
|
j = Jp2k(tfile.name, 'wb')
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
j.write(data, cinema2k=48)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "no write support on windows, period")
|
@unittest.skipIf(os.name == "nt", "no write support on windows, period")
|
||||||
@unittest.skipIf(re.match(r"""1\.[01234]\.\d""",
|
|
||||||
glymur.version.openjpeg_version) is not None,
|
|
||||||
"Writing only supported with openjpeg version 1.5+.")
|
|
||||||
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
|
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_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 TestSuiteWrite(unittest.TestCase):
|
class TestSuiteWrite(unittest.TestCase):
|
||||||
"""Tests for writing with openjp2 backend.
|
"""Tests for writing with openjp2 backend.
|
||||||
|
|
||||||
These tests roughly correspond with those tests with similar names in the
|
These tests either roughly correspond with those tests with similar names
|
||||||
OpenJPEG test suite.
|
in the OpenJPEG test suite or are closely associated.
|
||||||
"""
|
"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
pass
|
pass
|
||||||
|
|
@ -852,5 +1079,6 @@ class TestSuiteWrite(unittest.TestCase):
|
||||||
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
|
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
|
||||||
self.assertEqual(len(codestream.segment[2].spcod), 9)
|
self.assertEqual(len(codestream.segment[2].spcod), 9)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,14 @@ class TestPrinting(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_version_info(self):
|
||||||
|
"""Should be able to print(glymur.version.info)"""
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(glymur.version.info)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||||
def test_unknown_superbox(self):
|
def test_unknown_superbox(self):
|
||||||
"""Verify that we can handle an unknown superbox."""
|
"""Verify that we can handle an unknown superbox."""
|
||||||
|
|
@ -60,6 +68,9 @@ class TestPrinting(unittest.TestCase):
|
||||||
# Add the header for an unknwon superbox.
|
# Add the header for an unknwon superbox.
|
||||||
write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
|
write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
|
||||||
tfile.write(write_buffer)
|
tfile.write(write_buffer)
|
||||||
|
|
||||||
|
# Add a free box inside of it. We won't be able to identify it,
|
||||||
|
# but it's there.
|
||||||
write_buffer = struct.pack('>I4sI', 12, 'free'.encode(), 0)
|
write_buffer = struct.pack('>I4sI', 12, 'free'.encode(), 0)
|
||||||
tfile.write(write_buffer)
|
tfile.write(write_buffer)
|
||||||
tfile.flush()
|
tfile.flush()
|
||||||
|
|
@ -70,8 +81,7 @@ class TestPrinting(unittest.TestCase):
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
print(jpx.box[-1])
|
print(jpx.box[-1])
|
||||||
actual = fake_out.getvalue().strip()
|
actual = fake_out.getvalue().strip()
|
||||||
lines = ['Unknown Box (grp ) @ (695609, 20)',
|
lines = ["Unknown Box (b'grp ') @ (1399071, 20)"]
|
||||||
' Free Box (free) @ (695617, 12)']
|
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
@ -133,7 +143,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
lst = lst[1:]
|
lst = lst[1:]
|
||||||
actual = '\n'.join(lst)
|
actual = '\n'.join(lst)
|
||||||
expected = fixtures.nemo_dump_no_xml
|
expected = fixtures.nemo_dump_no_xml
|
||||||
self.maxDiff = None
|
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
def test_printoptions_short(self):
|
def test_printoptions_short(self):
|
||||||
|
|
@ -290,69 +299,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_crg(self):
|
|
||||||
"""verify printing of CRG segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p0_03.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[-5])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['CRG marker segment @ (87, 6)',
|
|
||||||
' Vertical, Horizontal offset: (0.50, 1.00)']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_rgn(self):
|
|
||||||
"""verify printing of RGN segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p0_03.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream(header_only=False)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[12])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['RGN marker segment @ (310, 5)',
|
|
||||||
' Associated component: 0',
|
|
||||||
' ROI style: 0',
|
|
||||||
' Parameter: 7']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_sop(self):
|
|
||||||
"""verify printing of SOP segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p0_03.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream(header_only=False)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[-2])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['SOP marker segment @ (12836, 4)',
|
|
||||||
' Nsop: 15']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_cme(self):
|
|
||||||
"""Test printing a CME or comment marker segment."""
|
|
||||||
filename = opj_data_file('input/conformance/p0_02.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
# 2nd to last segment in the main header
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[-2])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['CME marker segment @ (85, 45)',
|
|
||||||
' "Creator: AV-J2K (c) 2000,2001 Algo Vision"']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
def test_eoc_segment(self):
|
def test_eoc_segment(self):
|
||||||
"""verify printing of eoc segment"""
|
"""verify printing of eoc segment"""
|
||||||
j = glymur.Jp2k(self.jp2file)
|
j = glymur.Jp2k(self.jp2file)
|
||||||
|
|
@ -365,91 +311,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_plt_segment(self):
|
|
||||||
"""verify printing of PLT segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p0_07.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream(header_only=False)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[49935])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
|
|
||||||
lines = ['PLT marker segment @ (7871146, 38)',
|
|
||||||
' Index: 0',
|
|
||||||
' Iplt: [9, 122, 19, 30, 27, 9, 41, 62, 18, 29, 261,'
|
|
||||||
+ ' 55, 82, 299, 93, 941, 951, 687, 1729, 1443, 1008, 2168,'
|
|
||||||
+ ' 2188, 2223]']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_pod_segment(self):
|
|
||||||
"""verify printing of POD segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p0_13.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[8])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
|
|
||||||
lines = ['POD marker segment @ (878, 20)',
|
|
||||||
' Progression change 0:',
|
|
||||||
' Resolution index start: 0',
|
|
||||||
' Component index start: 0',
|
|
||||||
' Layer index end: 1',
|
|
||||||
' Resolution index end: 33',
|
|
||||||
' Component index end: 128',
|
|
||||||
' Progression order: RLCP',
|
|
||||||
' Progression change 1:',
|
|
||||||
' Resolution index start: 0',
|
|
||||||
' Component index start: 128',
|
|
||||||
' Layer index end: 1',
|
|
||||||
' Resolution index end: 33',
|
|
||||||
' Component index end: 257',
|
|
||||||
' Progression order: CPRL']
|
|
||||||
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_ppm_segment(self):
|
|
||||||
"""verify printing of PPM segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p1_03.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[9])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
|
|
||||||
lines = ['PPM marker segment @ (213, 43712)',
|
|
||||||
' Index: 0',
|
|
||||||
' Data: 43709 uninterpreted bytes']
|
|
||||||
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_ppt_segment(self):
|
|
||||||
"""verify printing of ppt segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p1_06.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream(header_only=False)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[6])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
|
|
||||||
lines = ['PPT marker segment @ (155, 109)',
|
|
||||||
' Index: 0',
|
|
||||||
' Packet headers: 106 uninterpreted bytes']
|
|
||||||
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
def test_qcc_segment(self):
|
def test_qcc_segment(self):
|
||||||
"""verify printing of qcc segment"""
|
"""verify printing of qcc segment"""
|
||||||
j = glymur.Jp2k(self.jp2file)
|
j = glymur.Jp2k(self.jp2file)
|
||||||
|
|
@ -490,7 +351,7 @@ class TestPrinting(unittest.TestCase):
|
||||||
actual = fake_out.getvalue().strip()
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
lines = ['SIZ marker segment @ (3233, 47)',
|
lines = ['SIZ marker segment @ (3233, 47)',
|
||||||
' Profile: 2',
|
' Profile: no profile',
|
||||||
' Reference Grid Height, Width: (1456 x 2592)',
|
' Reference Grid Height, Width: (1456 x 2592)',
|
||||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||||
' Reference Tile Height, Width: (1456 x 2592)',
|
' Reference Tile Height, Width: (1456 x 2592)',
|
||||||
|
|
@ -544,25 +405,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_tlm_segment(self):
|
|
||||||
"""verify printing of TLM segment"""
|
|
||||||
filename = opj_data_file('input/conformance/p0_15.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[10])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
|
|
||||||
lines = ['TLM marker segment @ (268, 28)',
|
|
||||||
' Index: 0',
|
|
||||||
' Tile number: (0, 1, 2, 3)',
|
|
||||||
' Length: (4267, 2117, 4080, 2081)']
|
|
||||||
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
def test_xmp(self):
|
def test_xmp(self):
|
||||||
"""Verify the printing of a UUID/XMP box."""
|
"""Verify the printing of a UUID/XMP box."""
|
||||||
j = glymur.Jp2k(self.jp2file)
|
j = glymur.Jp2k(self.jp2file)
|
||||||
|
|
@ -582,7 +424,7 @@ class TestPrinting(unittest.TestCase):
|
||||||
lst = ['Codestream:',
|
lst = ['Codestream:',
|
||||||
' SOC marker segment @ (3231, 0)',
|
' SOC marker segment @ (3231, 0)',
|
||||||
' SIZ marker segment @ (3233, 47)',
|
' SIZ marker segment @ (3233, 47)',
|
||||||
' Profile: 2',
|
' Profile: no profile',
|
||||||
' Reference Grid Height, Width: (1456 x 2592)',
|
' Reference Grid Height, Width: (1456 x 2592)',
|
||||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||||
' Reference Tile Height, Width: (1456 x 2592)',
|
' Reference Tile Height, Width: (1456 x 2592)',
|
||||||
|
|
@ -622,17 +464,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lst)
|
expected = '\n'.join(lst)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_xml(self):
|
|
||||||
"""verify printing of XML box"""
|
|
||||||
filename = opj_data_file('input/conformance/file1.jp2')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(j.box[2])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
self.assertEqual(actual, fixtures.file1_xml)
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000,
|
@unittest.skipIf(sys.hexversion < 0x03000000,
|
||||||
"Only trusting python3 for printing non-ascii chars")
|
"Only trusting python3 for printing non-ascii chars")
|
||||||
def test_xml_latin1(self):
|
def test_xml_latin1(self):
|
||||||
|
|
@ -690,100 +521,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_channel_definition(self):
|
|
||||||
"""verify printing of cdef box"""
|
|
||||||
filename = opj_data_file('input/conformance/file2.jp2')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(j.box[2].box[2])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['Channel Definition Box (cdef) @ (81, 28)',
|
|
||||||
' Channel 0 (color) ==> (3)',
|
|
||||||
' Channel 1 (color) ==> (2)',
|
|
||||||
' Channel 2 (color) ==> (1)']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_component_mapping(self):
|
|
||||||
"""verify printing of cmap box"""
|
|
||||||
filename = opj_data_file('input/conformance/file9.jp2')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(j.box[2].box[2])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['Component Mapping Box (cmap) @ (848, 20)',
|
|
||||||
' Component 0 ==> palette column 0',
|
|
||||||
' Component 0 ==> palette column 1',
|
|
||||||
' Component 0 ==> palette column 2']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_palette7(self):
|
|
||||||
"""verify printing of pclr box"""
|
|
||||||
filename = opj_data_file('input/conformance/file9.jp2')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(j.box[2].box[1])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['Palette Box (pclr) @ (66, 782)',
|
|
||||||
' Size: (256 x 3)']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_rreq(self):
|
|
||||||
"""verify printing of reader requirements box"""
|
|
||||||
filename = opj_data_file('input/conformance/file7.jp2')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(j.box[2])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
self.assertEqual(actual, fixtures.file7_rreq)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_differing_subsamples(self):
|
|
||||||
"""verify printing of SIZ with different subsampling... Issue 86."""
|
|
||||||
filename = opj_data_file('input/conformance/p0_05.j2k')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
codestream = j.get_codestream()
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(codestream.segment[1])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['SIZ marker segment @ (2, 50)',
|
|
||||||
' Profile: 0',
|
|
||||||
' Reference Grid Height, Width: (1024 x 1024)',
|
|
||||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
|
||||||
' Reference Tile Height, Width: (1024 x 1024)',
|
|
||||||
' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
|
|
||||||
' Bitdepth: (8, 8, 8, 8)',
|
|
||||||
' Signed: (False, False, False, False)',
|
|
||||||
' Vertical, Horizontal Subsampling: '
|
|
||||||
+ '((1, 1), (1, 1), (2, 2), (2, 2))']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_palette_box(self):
|
|
||||||
"""Verify that palette (pclr) boxes are printed without error."""
|
|
||||||
filename = opj_data_file('input/conformance/file9.jp2')
|
|
||||||
j = glymur.Jp2k(filename)
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(j.box[2].box[1])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['Palette Box (pclr) @ (66, 782)',
|
|
||||||
' Size: (256 x 3)']
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
def test_less_common_boxes(self):
|
def test_less_common_boxes(self):
|
||||||
"""verify uinf, ulst, url, res, resd, resc box printing"""
|
"""verify uinf, ulst, url, res, resd, resc box printing"""
|
||||||
|
|
@ -861,50 +598,6 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_icc_profile(self):
|
|
||||||
"""verify icc profile printing with a jpx"""
|
|
||||||
# ICC profiles may be used in JP2, but the approximation field should
|
|
||||||
# be zero unless we have jpx. This file does both.
|
|
||||||
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
# brand is 'jp2 ', but has any icc profile.
|
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
jp2 = Jp2k(filename)
|
|
||||||
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(jp2.box[3].box[1])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
if sys.hexversion < 0x03000000:
|
|
||||||
expected = text_gbr_27
|
|
||||||
elif sys.hexversion < 0x03040000:
|
|
||||||
expected = text_gbr_33
|
|
||||||
else:
|
|
||||||
expected = text_gbr_34
|
|
||||||
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
|
||||||
def test_uuid(self):
|
|
||||||
"""verify printing of UUID box"""
|
|
||||||
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
# brand is 'jp2 ', but has any icc profile.
|
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
jp2 = Jp2k(filename)
|
|
||||||
|
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
|
||||||
print(jp2.box[4])
|
|
||||||
actual = fake_out.getvalue().strip()
|
|
||||||
lines = ['UUID Box (uuid) @ (1544, 25)',
|
|
||||||
' UUID: 3a0d0218-0ae9-4115-b376-4bca41ce0e71 (unknown)',
|
|
||||||
' UUID Data: 1 bytes']
|
|
||||||
|
|
||||||
expected = '\n'.join(lines)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000,
|
@unittest.skipIf(sys.hexversion < 0x03000000,
|
||||||
"Ordered dicts not printing well in 2.7")
|
"Ordered dicts not printing well in 2.7")
|
||||||
def test_exif_uuid(self):
|
def test_exif_uuid(self):
|
||||||
|
|
@ -946,5 +639,395 @@ class TestPrinting(unittest.TestCase):
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
|
class TestPrintingOpjDataRoot(unittest.TestCase):
|
||||||
|
"""Tests for verifying printing. restricted to OPJ_DATA_ROOT files."""
|
||||||
|
def setUp(self):
|
||||||
|
self.jpxfile = glymur.data.jpxfile()
|
||||||
|
self.jp2file = glymur.data.nemo()
|
||||||
|
self.j2kfile = glymur.data.goodstuff()
|
||||||
|
|
||||||
|
# Reset printoptions for every test.
|
||||||
|
glymur.set_printoptions(short=False, xml=True, codestream=True)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_cinema_profile(self):
|
||||||
|
"""Should print Cinema 2K when the profile is 3."""
|
||||||
|
filename = opj_data_file('input/nonregression/_00042.j2k')
|
||||||
|
j2k = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
c = j2k.get_codestream()
|
||||||
|
print(c.segment[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.cinema2k_profile)
|
||||||
|
|
||||||
|
def test_invalid_colorspace(self):
|
||||||
|
"""An invalid colorspace shouldn't cause an error."""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jp2)
|
||||||
|
|
||||||
|
def test_bad_rsiz(self):
|
||||||
|
"""Should still be able to print if rsiz is bad, issue196"""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j)
|
||||||
|
|
||||||
|
def test_bad_wavelet_transform(self):
|
||||||
|
"""Should still be able to print if wavelet xform is bad, issue195"""
|
||||||
|
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j)
|
||||||
|
|
||||||
|
def test_invalid_progression_order(self):
|
||||||
|
"""Should still be able to print even if prog order is invalid."""
|
||||||
|
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(jfile)
|
||||||
|
codestream = jp2.get_codestream()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.issue_186_progression_order)
|
||||||
|
|
||||||
|
def test_crg(self):
|
||||||
|
"""verify printing of CRG segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p0_03.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[-5])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['CRG marker segment @ (87, 6)',
|
||||||
|
' Vertical, Horizontal offset: (0.50, 1.00)']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_rgn(self):
|
||||||
|
"""verify printing of RGN segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p0_03.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream(header_only=False)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[12])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['RGN marker segment @ (310, 5)',
|
||||||
|
' Associated component: 0',
|
||||||
|
' ROI style: 0',
|
||||||
|
' Parameter: 7']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_sop(self):
|
||||||
|
"""verify printing of SOP segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p0_03.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream(header_only=False)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[-2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['SOP marker segment @ (12836, 4)',
|
||||||
|
' Nsop: 15']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_cme(self):
|
||||||
|
"""Test printing a CME or comment marker segment."""
|
||||||
|
filename = opj_data_file('input/conformance/p0_02.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
# 2nd to last segment in the main header
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[-2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['CME marker segment @ (85, 45)',
|
||||||
|
' "Creator: AV-J2K (c) 2000,2001 Algo Vision"']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_plt_segment(self):
|
||||||
|
"""verify printing of PLT segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p0_07.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream(header_only=False)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[49935])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
|
lines = ['PLT marker segment @ (7871146, 38)',
|
||||||
|
' Index: 0',
|
||||||
|
' Iplt: [9, 122, 19, 30, 27, 9, 41, 62, 18, 29, 261,'
|
||||||
|
+ ' 55, 82, 299, 93, 941, 951, 687, 1729, 1443, 1008, 2168,'
|
||||||
|
+ ' 2188, 2223]']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_pod_segment(self):
|
||||||
|
"""verify printing of POD segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p0_13.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[8])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
|
lines = ['POD marker segment @ (878, 20)',
|
||||||
|
' Progression change 0:',
|
||||||
|
' Resolution index start: 0',
|
||||||
|
' Component index start: 0',
|
||||||
|
' Layer index end: 1',
|
||||||
|
' Resolution index end: 33',
|
||||||
|
' Component index end: 128',
|
||||||
|
' Progression order: RLCP',
|
||||||
|
' Progression change 1:',
|
||||||
|
' Resolution index start: 0',
|
||||||
|
' Component index start: 128',
|
||||||
|
' Layer index end: 1',
|
||||||
|
' Resolution index end: 33',
|
||||||
|
' Component index end: 257',
|
||||||
|
' Progression order: CPRL']
|
||||||
|
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_ppm_segment(self):
|
||||||
|
"""verify printing of PPM segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p1_03.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[9])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
|
lines = ['PPM marker segment @ (213, 43712)',
|
||||||
|
' Index: 0',
|
||||||
|
' Data: 43709 uninterpreted bytes']
|
||||||
|
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_ppt_segment(self):
|
||||||
|
"""verify printing of ppt segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p1_06.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream(header_only=False)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[6])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
|
lines = ['PPT marker segment @ (155, 109)',
|
||||||
|
' Index: 0',
|
||||||
|
' Packet headers: 106 uninterpreted bytes']
|
||||||
|
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_tlm_segment(self):
|
||||||
|
"""verify printing of TLM segment"""
|
||||||
|
filename = opj_data_file('input/conformance/p0_15.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[10])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
|
||||||
|
lines = ['TLM marker segment @ (268, 28)',
|
||||||
|
' Index: 0',
|
||||||
|
' Tile number: (0, 1, 2, 3)',
|
||||||
|
' Length: (4267, 2117, 4080, 2081)']
|
||||||
|
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_xml(self):
|
||||||
|
"""verify printing of XML box"""
|
||||||
|
filename = opj_data_file('input/conformance/file1.jp2')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j.box[2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.file1_xml)
|
||||||
|
|
||||||
|
def test_channel_definition(self):
|
||||||
|
"""verify printing of cdef box"""
|
||||||
|
filename = opj_data_file('input/conformance/file2.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# Bad compatibility list item.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j.box[2].box[2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['Channel Definition Box (cdef) @ (81, 28)',
|
||||||
|
' Channel 0 (color) ==> (3)',
|
||||||
|
' Channel 1 (color) ==> (2)',
|
||||||
|
' Channel 2 (color) ==> (1)']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_component_mapping(self):
|
||||||
|
"""verify printing of cmap box"""
|
||||||
|
filename = opj_data_file('input/conformance/file9.jp2')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j.box[2].box[2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['Component Mapping Box (cmap) @ (848, 20)',
|
||||||
|
' Component 0 ==> palette column 0',
|
||||||
|
' Component 0 ==> palette column 1',
|
||||||
|
' Component 0 ==> palette column 2']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_palette7(self):
|
||||||
|
"""verify printing of pclr box"""
|
||||||
|
filename = opj_data_file('input/conformance/file9.jp2')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j.box[2].box[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['Palette Box (pclr) @ (66, 782)',
|
||||||
|
' Size: (256 x 3)']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
@unittest.skip("file7 no longer has a rreq")
|
||||||
|
def test_rreq(self):
|
||||||
|
"""verify printing of reader requirements box"""
|
||||||
|
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j.box[2])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.text_GBR_rreq)
|
||||||
|
|
||||||
|
def test_differing_subsamples(self):
|
||||||
|
"""verify printing of SIZ with different subsampling... Issue 86."""
|
||||||
|
filename = opj_data_file('input/conformance/p0_05.j2k')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
codestream = j.get_codestream()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(codestream.segment[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['SIZ marker segment @ (2, 50)',
|
||||||
|
' Profile: 0',
|
||||||
|
' Reference Grid Height, Width: (1024 x 1024)',
|
||||||
|
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||||
|
' Reference Tile Height, Width: (1024 x 1024)',
|
||||||
|
' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
|
||||||
|
' Bitdepth: (8, 8, 8, 8)',
|
||||||
|
' Signed: (False, False, False, False)',
|
||||||
|
' Vertical, Horizontal Subsampling: '
|
||||||
|
+ '((1, 1), (1, 1), (2, 2), (2, 2))']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_palette_box(self):
|
||||||
|
"""Verify that palette (pclr) boxes are printed without error."""
|
||||||
|
filename = opj_data_file('input/conformance/file9.jp2')
|
||||||
|
j = glymur.Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j.box[2].box[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['Palette Box (pclr) @ (66, 782)',
|
||||||
|
' Size: (256 x 3)']
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_icc_profile(self):
|
||||||
|
"""verify icc profile printing with a jpx"""
|
||||||
|
# ICC profiles may be used in JP2, but the approximation field should
|
||||||
|
# be zero unless we have jpx. This file does both.
|
||||||
|
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# brand is 'jp2 ', but has any icc profile.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jp2.box[3].box[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
expected = text_gbr_27
|
||||||
|
elif sys.hexversion < 0x03040000:
|
||||||
|
expected = text_gbr_33
|
||||||
|
else:
|
||||||
|
expected = text_gbr_34
|
||||||
|
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_uuid(self):
|
||||||
|
"""verify printing of UUID box"""
|
||||||
|
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# brand is 'jp2 ', but has any icc profile.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jp2.box[4])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
lines = ['UUID Box (uuid) @ (1544, 25)',
|
||||||
|
' UUID: 3a0d0218-0ae9-4115-b376-4bca41ce0e71 (unknown)',
|
||||||
|
' UUID Data: 1 bytes']
|
||||||
|
|
||||||
|
expected = '\n'.join(lines)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_issue182(self):
|
||||||
|
"""Should not show the format string in output."""
|
||||||
|
# The cmap box is wildly broken, but printing was still wrong.
|
||||||
|
# Format strings like %d were showing up in the output.
|
||||||
|
filename = opj_data_file('input/nonregression/mem-b2ace68c-1381.jp2')
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# Ignore warning about bad pclr box.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jp2.box[3].box[3])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.issue_182_cmap)
|
||||||
|
|
||||||
|
def test_issue183(self):
|
||||||
|
filename = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# Ignore warning about bad pclr box.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jp2.box[2].box[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.issue_183_colr)
|
||||||
|
|
||||||
|
def test_bom(self):
|
||||||
|
"""Byte order markers are illegal in UTF-8. Issue 185"""
|
||||||
|
filename = opj_data_file(os.path.join('input',
|
||||||
|
'nonregression',
|
||||||
|
'issue171.jp2'))
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
# No need to verify, it's enough that we don't error out.
|
||||||
|
print(jp2)
|
||||||
|
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ License: MIT
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import numpy as np
|
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
|
import lxml.etree
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from .lib import openjpeg as opj
|
from .lib import openjpeg as opj
|
||||||
from .lib import openjp2 as opj2
|
from .lib import openjp2 as opj2
|
||||||
|
|
||||||
|
|
@ -48,10 +50,12 @@ OPENJPEG {openjpeg}
|
||||||
Python {python}
|
Python {python}
|
||||||
sys.platform {platform}
|
sys.platform {platform}
|
||||||
sys.maxsize {maxsize}
|
sys.maxsize {maxsize}
|
||||||
|
lxml {elxml}
|
||||||
numpy {numpy}
|
numpy {numpy}
|
||||||
""".format(glymur=version,
|
""".format(glymur=version,
|
||||||
openjpeg=openjpeg_version,
|
openjpeg=openjpeg_version,
|
||||||
python=sys.version,
|
python=sys.version,
|
||||||
platform=sys.platform,
|
platform=sys.platform,
|
||||||
maxsize=sys.maxsize,
|
maxsize=sys.maxsize,
|
||||||
|
elxml=lxml.etree.__version__,
|
||||||
numpy=np.__version__)
|
numpy=np.__version__)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue