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
|
||||
install:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors lxml contextlib2 mock; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install --use-mirrors lxml numpy; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install lxml contextlib2 mock; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install lxml numpy; fi
|
||||
|
||||
# command to run tests
|
||||
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,
|
||||
Palette and Component Mapping boxes, JPX Association, NumberList
|
||||
and DataReference boxes. Added read support for JPX free,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ ChangeLog
|
|||
0.6.0 (pending)
|
||||
===============
|
||||
|
||||
* Added Cinema2K, Cinema4K write support.
|
||||
* Added lxml requirement.
|
||||
* added set_printoptions, get_printoptions function
|
||||
* 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 read/write support for JPX free, number list, and data reference 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.
|
||||
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
|
||||
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
|
||||
and running OpenJPEG's test suite, check their instructions for all this.
|
||||
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,
|
||||
you should use the 0.5 series of Glymur.
|
||||
|
||||
OpenJPEG Installation
|
||||
=====================
|
||||
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.
|
||||
For more information about OpenJPEG, please consult http://www.openjpeg.org.
|
||||
|
||||
Glymur Installation
|
||||
===================
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ def tiff_header(read_buffer):
|
|||
endian = '>'
|
||||
else:
|
||||
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])
|
||||
|
||||
|
|
|
|||
|
|
@ -27,19 +27,36 @@ import numpy as np
|
|||
from .core import LRCP, RLCP, RPCL, PCRL, CPRL
|
||||
from .core import WAVELET_XFORM_9X7_IRREVERSIBLE
|
||||
from .core import WAVELET_XFORM_5X3_REVERSIBLE
|
||||
from .core import _CAPABILITIES_DISPLAY
|
||||
from .core import _Keydefaultdict
|
||||
from .lib import openjp2 as opj2
|
||||
|
||||
_PROGRESSION_ORDER_DISPLAY = {
|
||||
LRCP: 'LRCP',
|
||||
RLCP: 'RLCP',
|
||||
RPCL: 'RPCL',
|
||||
PCRL: 'PCRL',
|
||||
CPRL: 'CPRL'}
|
||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||
_PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory,
|
||||
{ LRCP: 'LRCP',
|
||||
RLCP: 'RLCP',
|
||||
RPCL: 'RPCL',
|
||||
PCRL: 'PCRL',
|
||||
CPRL: 'CPRL'})
|
||||
|
||||
_WAVELET_TRANSFORM_DISPLAY = {
|
||||
WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible',
|
||||
WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'}
|
||||
_WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory,
|
||||
{ WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible',
|
||||
WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'})
|
||||
|
||||
_NO_PROFILE = 0
|
||||
_PROFILE_0 = 1
|
||||
_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.
|
||||
# See table A-1 in ISO/IEC FCD15444-1.
|
||||
|
|
@ -371,6 +388,14 @@ class Codestream(object):
|
|||
numbytes = offset + 2 + length - fptr.tell()
|
||||
spcod = fptr.read(numbytes)
|
||||
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
|
||||
eph = (scod & 4) > 0
|
||||
|
|
@ -645,10 +670,13 @@ class Codestream(object):
|
|||
read_buffer = fptr.read(2)
|
||||
length, = struct.unpack('>H', read_buffer)
|
||||
|
||||
xy_buffer = fptr.read(36)
|
||||
data = struct.unpack('>HIIIIIIIIH', xy_buffer)
|
||||
read_buffer = fptr.read(length - 2)
|
||||
data = struct.unpack_from('>HIIIIIIIIH', read_buffer)
|
||||
|
||||
rsiz = data[0]
|
||||
if rsiz not in _KNOWN_PROFILES:
|
||||
warnings.warn("Invalid profile: (Rsiz={0}).".format(rsiz))
|
||||
|
||||
xysiz = (data[1], data[2])
|
||||
xyosiz = (data[3], data[4])
|
||||
xytsiz = (data[5], data[6])
|
||||
|
|
@ -657,9 +685,8 @@ class Codestream(object):
|
|||
# Csiz is the number of components
|
||||
Csiz = data[9]
|
||||
|
||||
component_buffer = fptr.read(Csiz * 3)
|
||||
data = struct.unpack('>' + 'B' * len(component_buffer),
|
||||
component_buffer)
|
||||
data = struct.unpack_from('>' + 'B' * (length - 36 - 2),
|
||||
read_buffer, offset=36)
|
||||
|
||||
bitdepth = tuple(((x & 0x7f) + 1) 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])
|
||||
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,
|
||||
'xysiz': xysiz,
|
||||
'xyosiz': xyosiz,
|
||||
|
|
@ -1514,14 +1553,6 @@ class SIZsegment(Segment):
|
|||
lst.append(bitdepth - 1)
|
||||
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):
|
||||
msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, "
|
||||
msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, "
|
||||
|
|
|
|||
|
|
@ -1,8 +1,22 @@
|
|||
"""Core definitions to be shared amongst the modules.
|
||||
"""
|
||||
import collections
|
||||
import copy
|
||||
import lxml.etree as ET
|
||||
|
||||
class _Keydefaultdict(collections.defaultdict):
|
||||
"""Unlisted keys help form their own error message.
|
||||
|
||||
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
|
||||
LRCP = 0
|
||||
RLCP = 1
|
||||
|
|
@ -10,6 +24,26 @@ RPCL = 2
|
|||
PCRL = 3
|
||||
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 = {
|
||||
'LRCP': LRCP,
|
||||
'RLCP': RLCP,
|
||||
|
|
@ -37,24 +71,26 @@ YCC = 18
|
|||
E_SRGB = 20
|
||||
ROMM_RGB = 21
|
||||
|
||||
_COLORSPACE_MAP_DISPLAY = {
|
||||
CMYK: 'CMYK',
|
||||
SRGB: 'sRGB',
|
||||
GREYSCALE: 'greyscale',
|
||||
YCC: 'YCC',
|
||||
E_SRGB: 'e-sRGB',
|
||||
ROMM_RGB: 'ROMM-RGB'}
|
||||
_factory = lambda x: '{0} (unrecognized)'.format(x)
|
||||
_COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
||||
{ CMYK: 'CMYK',
|
||||
SRGB: 'sRGB',
|
||||
GREYSCALE: 'greyscale',
|
||||
YCC: 'YCC',
|
||||
E_SRGB: 'e-sRGB',
|
||||
ROMM_RGB: 'ROMM-RGB'} )
|
||||
|
||||
# enumerated color channel types
|
||||
COLOR = 0
|
||||
OPACITY = 1
|
||||
PRE_MULTIPLIED_OPACITY = 2
|
||||
_UNSPECIFIED = 65535
|
||||
_COLOR_TYPE_MAP_DISPLAY = {
|
||||
COLOR: 'color',
|
||||
OPACITY: 'opacity',
|
||||
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
||||
_UNSPECIFIED: 'unspecified'}
|
||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||
_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory,
|
||||
{ COLOR: 'color',
|
||||
OPACITY: 'opacity',
|
||||
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
||||
_UNSPECIFIED: 'unspecified'})
|
||||
|
||||
# color channel definitions.
|
||||
RED = 1
|
||||
|
|
@ -70,9 +106,3 @@ _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3},
|
|||
E_SRGB: {"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():
|
||||
"""Shortcut for specifying path to 12-v6.4.jpx.
|
||||
"""Shortcut for specifying path to heliov.jpx.
|
||||
|
||||
Returns
|
||||
-------
|
||||
file : str
|
||||
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
|
||||
|
||||
|
|
|
|||
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 math
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
from uuid import UUID
|
||||
import warnings
|
||||
|
|
@ -28,7 +29,7 @@ import numpy as np
|
|||
|
||||
from .codestream import Codestream
|
||||
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 .jp2box import Jp2kBox
|
||||
from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox
|
||||
|
|
@ -153,6 +154,36 @@ class Jp2k(Jp2kBox):
|
|||
msg += "profile if the file type box brand is 'jp2 '."
|
||||
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):
|
||||
"""Populate compression parameters structure from input arguments.
|
||||
|
||||
|
|
@ -219,6 +250,14 @@ class Jp2k(Jp2kBox):
|
|||
cparams.tcp_numlayers = 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:
|
||||
cparams.cblockw_init = kwargs['cbsize'][1]
|
||||
cparams.cblockh_init = kwargs['cbsize'][0]
|
||||
|
|
@ -298,6 +337,10 @@ class Jp2k(Jp2kBox):
|
|||
colorspace : int
|
||||
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:
|
||||
msg = "Cannot specify cratios and psnr together."
|
||||
|
|
@ -340,6 +383,10 @@ class Jp2k(Jp2kBox):
|
|||
Image data to be written to file.
|
||||
cbsize : tuple, optional
|
||||
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
|
||||
Either 'rgb' or 'gray'.
|
||||
cratios : iterable
|
||||
|
|
@ -395,7 +442,7 @@ class Jp2k(Jp2kBox):
|
|||
If glymur is unable to load the openjp2 library.
|
||||
"""
|
||||
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:
|
||||
self._write_openjpeg(img_array, verbose=verbose, **kwargs)
|
||||
else:
|
||||
|
|
@ -473,7 +520,7 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
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)
|
||||
|
||||
|
|
@ -559,7 +606,12 @@ class Jp2k(Jp2kBox):
|
|||
self.parse()
|
||||
|
||||
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
|
||||
----------
|
||||
|
|
@ -569,6 +621,8 @@ class Jp2k(Jp2kBox):
|
|||
JP2 box definitions to define the JP2 file format. If not
|
||||
provided, a default ""jacket" is assumed, consisting of JP2
|
||||
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
|
||||
-------
|
||||
|
|
@ -584,19 +638,7 @@ class Jp2k(Jp2kBox):
|
|||
>>> jp2 = j2k.wrap(tfile.name)
|
||||
"""
|
||||
if boxes is None:
|
||||
# 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)
|
||||
boxes[2].box = [ImageHeaderBox(height=height,
|
||||
width=width,
|
||||
num_components=num_components),
|
||||
ColourSpecificationBox(colorspace=SRGB)]
|
||||
boxes = self._get_default_jp2_boxes()
|
||||
|
||||
_validate_jp2_box_sequence(boxes)
|
||||
|
||||
|
|
@ -605,34 +647,92 @@ class Jp2k(Jp2kBox):
|
|||
if box.box_id != 'jp2c':
|
||||
box.write(ofile)
|
||||
else:
|
||||
# The codestream gets written last.
|
||||
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))
|
||||
|
||||
self._write_wrapped_codestream(ofile, box)
|
||||
ofile.flush()
|
||||
|
||||
jp2 = Jp2k(filename)
|
||||
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):
|
||||
"""Read a JPEG 2000 image.
|
||||
|
||||
|
|
@ -649,6 +749,9 @@ class Jp2k(Jp2kBox):
|
|||
(first_row, first_col, last_row, last_col)
|
||||
tile : int, optional
|
||||
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
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
|
||||
|
|
@ -703,7 +806,8 @@ class Jp2k(Jp2kBox):
|
|||
msg += "the read_bands method instead."
|
||||
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.
|
||||
|
||||
Parameters
|
||||
|
|
@ -711,6 +815,9 @@ class Jp2k(Jp2kBox):
|
|||
rlevel : int, optional
|
||||
Factor by which to rlevel output resolution. Use -1 to get the
|
||||
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
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
|
||||
|
|
@ -726,32 +833,12 @@ class Jp2k(Jp2kBox):
|
|||
"""
|
||||
self._subsampling_sanity_check()
|
||||
|
||||
if rlevel != 0:
|
||||
# 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)
|
||||
dparameters = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef)
|
||||
|
||||
with ExitStack() as stack:
|
||||
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
|
||||
|
||||
infile = self.filename.encode()
|
||||
nelts = opj.PATH_LEN - len(infile)
|
||||
infile += b'0' * nelts
|
||||
dparameters.infile = infile
|
||||
|
||||
dinfo = opj.create_decompress(dparameters.decod_format)
|
||||
|
||||
event_mgr = opj.EventMgrType()
|
||||
|
|
@ -788,7 +875,7 @@ class Jp2k(Jp2kBox):
|
|||
return data
|
||||
|
||||
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.
|
||||
|
||||
Parameters
|
||||
|
|
@ -818,7 +905,8 @@ class Jp2k(Jp2kBox):
|
|||
"""
|
||||
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:
|
||||
if hasattr(opj2.OPENJP2,
|
||||
|
|
@ -862,27 +950,35 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
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.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
layer : int, optional
|
||||
layer : int
|
||||
Number of quality layer to decode.
|
||||
rlevel : int, optional
|
||||
rlevel : int
|
||||
Factor by which to rlevel output resolution.
|
||||
area : tuple, optional
|
||||
area : tuple
|
||||
Specifies decoding image area,
|
||||
(first_row, first_col, last_row, last_col)
|
||||
tile : int, optional
|
||||
tile : int
|
||||
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
|
||||
-------
|
||||
dparam : DecompressionParametersType (ctypes)
|
||||
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()
|
||||
nelts = opj2.PATH_LEN - len(infile)
|
||||
|
|
@ -891,12 +987,22 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
dparam.decod_format = self._codec_format
|
||||
|
||||
dparam.cp_layer = layer
|
||||
if layer is not None:
|
||||
dparam.cp_layer = layer
|
||||
|
||||
if rlevel == -1:
|
||||
# Get the lowest resolution thumbnail.
|
||||
# Must check the specified rlevel against the maximum.
|
||||
if rlevel != 0:
|
||||
# Must check the specified rlevel against the maximum.
|
||||
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
|
||||
|
||||
if area is not None:
|
||||
|
|
@ -913,10 +1019,14 @@ class Jp2k(Jp2kBox):
|
|||
dparam.tile_index = tile
|
||||
dparam.nb_tile_to_decode = 1
|
||||
|
||||
if ignore_pclr_cmap_cdef is True:
|
||||
# Return raw codestream components.
|
||||
dparam.flags |= 1
|
||||
|
||||
return dparam
|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
tile : int, optional
|
||||
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
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
|
||||
|
|
@ -963,7 +1076,8 @@ class Jp2k(Jp2kBox):
|
|||
"of OpenJP2 installed before using "
|
||||
"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:
|
||||
if hasattr(opj2.OPENJP2,
|
||||
|
|
@ -1025,7 +1139,7 @@ class Jp2k(Jp2kBox):
|
|||
>>> codestream = jp2.get_codestream()
|
||||
>>> print(codestream.segment[1])
|
||||
SIZ marker segment @ (3233, 47)
|
||||
Profile: 2
|
||||
Profile: no profile
|
||||
Reference Grid Height, Width: (1456 x 2592)
|
||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||
Reference Tile Height, Width: (1456 x 2592)
|
||||
|
|
@ -1125,12 +1239,26 @@ def _validate_jp2_box_sequence(boxes):
|
|||
if boxes[1].brand == 'jpx ':
|
||||
_validate_jpx_box_sequence(boxes)
|
||||
else:
|
||||
# Validate the JP2 box IDs.
|
||||
count = _collect_box_count(boxes)
|
||||
for id in count.keys():
|
||||
if id not in JP2_IDS:
|
||||
for box_id in count.keys():
|
||||
if box_id not in JP2_IDS:
|
||||
msg = "The presence of a '{0}' box requires that the file type "
|
||||
msg += "brand be set to 'jpx '."
|
||||
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):
|
||||
"""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."""
|
||||
box_ids = set([box.box_id for box in boxes])
|
||||
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."
|
||||
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.
|
||||
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)
|
||||
dest = image.contents.comps[k].data
|
||||
src = layer.ctypes.data
|
||||
|
|
|
|||
|
|
@ -10,6 +10,20 @@ import sys
|
|||
from .config import 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 = []
|
||||
|
||||
# Map certain atomic OpenJPEG datatypes to the ctypes equivalents.
|
||||
|
|
@ -35,6 +49,7 @@ CLRSPC_UNSPECIFIED = 0
|
|||
CLRSPC_SRGB = 1
|
||||
CLRSPC_GRAY = 2
|
||||
CLRSPC_YCC = 3
|
||||
CLRSPC_EYCC = 4
|
||||
COLOR_SPACE_TYPE = ctypes.c_int
|
||||
|
||||
# supported codec
|
||||
|
|
@ -392,6 +407,8 @@ class ImageCompType(ctypes.Structure):
|
|||
# image component data
|
||||
("data", ctypes.POINTER(ctypes.c_int32))]
|
||||
|
||||
if _MINOR == '1':
|
||||
_fields_.append(("alpha", ctypes.c_uint16))
|
||||
|
||||
class ImageType(ctypes.Structure):
|
||||
"""Defines image data and characteristics.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ try:
|
|||
HAS_PYTHON_XMP_TOOLKIT = True
|
||||
else:
|
||||
HAS_PYTHON_XMP_TOOLKIT = False
|
||||
except ImportError:
|
||||
except:
|
||||
HAS_PYTHON_XMP_TOOLKIT = False
|
||||
|
||||
# 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:
|
||||
SOC marker segment @ (3231, 0)
|
||||
SIZ marker segment @ (3233, 47)
|
||||
Profile: 2
|
||||
Profile: no profile
|
||||
Reference Grid Height, Width: (1456 x 2592)
|
||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||
Reference Tile Height, Width: (1456 x 2592)
|
||||
|
|
@ -477,7 +477,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
|
|||
Main header:
|
||||
SOC marker segment @ (3231, 0)
|
||||
SIZ marker segment @ (3233, 47)
|
||||
Profile: 2
|
||||
Profile: no profile
|
||||
Reference Grid Height, Width: (1456 x 2592)
|
||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||
Reference Tile Height, Width: (1456 x 2592)
|
||||
|
|
@ -554,15 +554,21 @@ UUID Box (uuid) @ (77, 3146)
|
|||
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
|
||||
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
|
||||
|
||||
# Output of reader requirement printing for file7.jp2
|
||||
file7_rreq = r"""Reader Requirements Box (rreq) @ (44, 24)
|
||||
Fully Understands Aspect Mask: 0xa0
|
||||
Display Completely Mask: 0xc0
|
||||
# Output of reader requirements printing for text_GBR.jp2
|
||||
text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109)
|
||||
Fully Understands Aspect Mask: 0xffff
|
||||
Display Completely Mask: 0xf8f0
|
||||
Standard Features and Masks:
|
||||
Feature 005: 0x80 Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 | ISO/IEC 15444-1
|
||||
Feature 060: 0x60 e-sRGB enumerated colorspace
|
||||
Feature 043: 0x40 Deprecated - compositing layer uses restricted ICC profile
|
||||
Vendor Features:"""
|
||||
Feature 001: 0x8000 Deprecated - contains no extensions
|
||||
Feature 005: 0x4080 Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 | ISO/IEC 15444-1
|
||||
Feature 012: 0x2040 Deprecated - codestream is contiguous
|
||||
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)
|
||||
<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</GENERAL_CREATION_INFO>
|
||||
</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 tempfile
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
from glymur import Jp2k
|
||||
import glymur
|
||||
|
||||
try:
|
||||
DATA_ROOT = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
DATA_ROOT = None
|
||||
except:
|
||||
raise
|
||||
|
||||
from .fixtures import opj_data_file, OPJ_DATA_ROOT
|
||||
|
||||
class TestCodestream(unittest.TestCase):
|
||||
"""Test suite for unusual codestream cases."""
|
||||
|
|
@ -34,8 +29,73 @@ class TestCodestream(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_siz_segment_ssiz_unsigned(self):
|
||||
"""ssiz attribute to be removed in future release"""
|
||||
j = Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
|
||||
# The ssiz attribute was simply a tuple of raw bytes.
|
||||
# The first 7 bits are interpreted as the bitdepth, the MSB determines
|
||||
# whether or not it is signed.
|
||||
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
||||
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
class TestCodestreamOpjData(unittest.TestCase):
|
||||
"""Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT"""
|
||||
|
||||
def setUp(self):
|
||||
self.jp2file = glymur.data.nemo()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
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.")
|
||||
def test_reserved_marker_segment(self):
|
||||
"""Reserved marker segments are ok."""
|
||||
|
|
@ -45,7 +105,7 @@ class TestCodestream(unittest.TestCase):
|
|||
#
|
||||
# Let's inject a reserved marker segment into a file that
|
||||
# we know something about to make sure we can still parse it.
|
||||
filename = os.path.join(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 open(filename, 'rb') as ifile:
|
||||
# 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].data, b'\x00')
|
||||
|
||||
@unittest.skipIf(DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
@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
|
||||
# be valid. We still parse the file, but warn about the offending
|
||||
# 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 open(filename, 'rb') as ifile:
|
||||
# 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].data, b'\x00')
|
||||
|
||||
@unittest.skipIf(DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_psot_is_zero(self):
|
||||
"""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')
|
||||
j = Jp2k(filename)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
|
|
@ -114,22 +170,9 @@ class TestCodestream(unittest.TestCase):
|
|||
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):
|
||||
"""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)
|
||||
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 sys
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -30,8 +31,11 @@ class TestICC(unittest.TestCase):
|
|||
def test_file5(self):
|
||||
"""basic ICC profile"""
|
||||
filename = opj_data_file('input/conformance/file5.jp2')
|
||||
j = Jp2k(filename)
|
||||
profile = j.box[3].box[1].icc_profile
|
||||
with warnings.catch_warnings():
|
||||
# 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['Preferred CMM Type'], 0)
|
||||
self.assertEqual(profile['Version'], '2.2.0')
|
||||
|
|
|
|||
|
|
@ -58,6 +58,23 @@ class TestDataEntryURL(unittest.TestCase):
|
|||
def setUp(self):
|
||||
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):
|
||||
"""Just your most basic 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)):
|
||||
j2k.wrap(tfile.name, boxes=boxes)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||
def test_bad_type(self):
|
||||
"""Channel types are limited to 0, 1, 2, 65535
|
||||
Should reject if not all of index, channel_type, association the
|
||||
|
|
@ -347,17 +365,18 @@ class TestChannelDefinition(unittest.TestCase):
|
|||
"""
|
||||
channel_type = (COLOR, COLOR, 3)
|
||||
association = (RED, GREEN, BLUE)
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
|
||||
association=association)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||
def test_wrong_lengths(self):
|
||||
"""Should reject if not all of index, channel_type, association the
|
||||
same length.
|
||||
"""
|
||||
channel_type = (COLOR, COLOR)
|
||||
association = (RED, GREEN, BLUE)
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
|
||||
association=association)
|
||||
|
||||
|
|
@ -373,14 +392,19 @@ class TestFileTypeBox(unittest.TestCase):
|
|||
|
||||
def test_brand_unknown(self):
|
||||
"""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 tempfile.TemporaryFile() as tfile:
|
||||
ftyp.write(tfile)
|
||||
|
||||
def test_cl_entry_unknown(self):
|
||||
"""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 tempfile.TemporaryFile() as tfile:
|
||||
ftyp.write(tfile)
|
||||
|
|
@ -429,6 +453,18 @@ class TestColourSpecificationBox(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
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):
|
||||
"""basic colr instantiation"""
|
||||
colr = ColourSpecificationBox(colorspace=glymur.core.SRGB)
|
||||
|
|
@ -438,27 +474,30 @@ class TestColourSpecificationBox(unittest.TestCase):
|
|||
self.assertEqual(colr.colorspace, glymur.core.SRGB)
|
||||
self.assertIsNone(colr.icc_profile)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
|
||||
def test_colr_with_cspace_and_icc(self):
|
||||
"""Colour specification boxes can't have both."""
|
||||
with self.assertRaises((OSError, IOError)):
|
||||
with self.assertWarns(UserWarning):
|
||||
colorspace = glymur.core.SRGB
|
||||
rawb = b'\x01\x02\x03\x04'
|
||||
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
||||
icc_profile=rawb)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
|
||||
def test_colr_with_bad_method(self):
|
||||
"""colr must have a valid method field"""
|
||||
colorspace = glymur.core.SRGB
|
||||
method = -1
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
||||
method=method)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
|
||||
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
|
||||
approx = -1
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
|
||||
approximation=approx)
|
||||
|
||||
|
|
@ -484,21 +523,23 @@ class TestPaletteBox(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||
def test_mismatched_bitdepth_signed(self):
|
||||
"""bitdepth and signed arguments must have equal length"""
|
||||
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
|
||||
bps = (8, 8, 8)
|
||||
signed = (False, False)
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
||||
signed=signed)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||
def test_mismatched_signed_palette(self):
|
||||
"""bitdepth and signed arguments must have equal length"""
|
||||
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
|
||||
bps = (8, 8, 8, 8)
|
||||
signed = (False, False, False, False)
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
||||
signed=signed)
|
||||
|
||||
|
|
@ -666,8 +707,9 @@ class TestWrap(unittest.TestCase):
|
|||
def test_jpx_to_jp2(self):
|
||||
"""basic test for rewrapping a jpx file"""
|
||||
jpx = Jp2k(self.jpxfile)
|
||||
idx = [0, 1, 3, 6]
|
||||
boxes = [jpx.box[idx] for idx in [0, 1, 3, 6]]
|
||||
# Use only the signature, file type, header, and 1st codestream.
|
||||
lst = [0, 1, 2, 5]
|
||||
boxes = [jpx.box[idx] for idx in lst]
|
||||
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||
jp2 = jpx.wrap(tfile.name, boxes=boxes)
|
||||
|
||||
|
|
@ -828,6 +870,67 @@ class TestWrap(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
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):
|
||||
"""Tests for canonical JP2 boxes."""
|
||||
|
|
|
|||
|
|
@ -8,18 +8,22 @@ import struct
|
|||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
import lxml.etree as ET
|
||||
|
||||
import glymur
|
||||
from glymur import Jp2k
|
||||
from glymur.jp2box import DataEntryURLBox, FileTypeBox, JPEG2000SignatureBox
|
||||
from glymur.jp2box import DataReferenceBox, FragmentListBox, FragmentTableBox
|
||||
from glymur.jp2box import ColourSpecificationBox
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
class TestJPXWrap(unittest.TestCase):
|
||||
"""Test suite for wrapping JPX files."""
|
||||
|
||||
def setUp(self):
|
||||
self.jpxfile = glymur.data.jpxfile()
|
||||
self.jp2file = glymur.data.nemo()
|
||||
self.j2kfile = glymur.data.goodstuff()
|
||||
|
||||
|
|
@ -107,6 +111,68 @@ class TestJPXWrap(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
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):
|
||||
"""Write a fragment table box."""
|
||||
# Add a negative test where offset < 0
|
||||
|
|
@ -207,13 +273,14 @@ class TestJPXWrap(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
jp2.wrap(tfile.name, boxes=boxes)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
|
||||
def test_deurl_child_of_dtbl(self):
|
||||
"""Data reference boxes can only contain data entry url boxes."""
|
||||
jp2 = Jp2k(self.jp2file)
|
||||
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
||||
|
||||
ftyp = glymur.jp2box.FileTypeBox()
|
||||
with self.assertRaises(IOError):
|
||||
with self.assertWarns(UserWarning):
|
||||
dref = glymur.jp2box.DataReferenceBox([ftyp])
|
||||
|
||||
# Try to get around it by appending the ftyp box after creation.
|
||||
|
|
@ -337,7 +404,9 @@ class TestJPX(unittest.TestCase):
|
|||
offset = [89]
|
||||
length = [1132288]
|
||||
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 tempfile.TemporaryFile() as tfile:
|
||||
flst.write(tfile)
|
||||
|
|
@ -347,8 +416,10 @@ class TestJPX(unittest.TestCase):
|
|||
offset = [0]
|
||||
length = [1132288]
|
||||
reference = [0]
|
||||
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
||||
with self.assertRaises(IOError):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
|
||||
with self.assertRaises((IOError, OSError)):
|
||||
with tempfile.TemporaryFile() as tfile:
|
||||
flst.write(tfile)
|
||||
|
||||
|
|
@ -357,14 +428,18 @@ class TestJPX(unittest.TestCase):
|
|||
offset = [89]
|
||||
length = [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 tempfile.TemporaryFile() as tfile:
|
||||
flst.write(tfile)
|
||||
|
||||
def test_ftbl_boxes_empty(self):
|
||||
"""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 tempfile.TemporaryFile() as tfile:
|
||||
ftbl.write(tfile)
|
||||
|
|
@ -377,6 +452,7 @@ class TestJPX(unittest.TestCase):
|
|||
with tempfile.TemporaryFile() as tfile:
|
||||
ftbl.write(tfile)
|
||||
|
||||
@unittest.skip("No such jpx file anymore.")
|
||||
def test_jpx_rreq_mask_length_3(self):
|
||||
"""There are some JPX files with rreq mask length of 3."""
|
||||
jpx = Jp2k(self.jpxfile)
|
||||
|
|
@ -386,7 +462,7 @@ class TestJPX(unittest.TestCase):
|
|||
self.assertEqual(jpx.box[2].standard_flag,
|
||||
(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):
|
||||
"""Verify that we can handle an unknown superbox."""
|
||||
with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile:
|
||||
|
|
@ -402,15 +478,9 @@ class TestJPX(unittest.TestCase):
|
|||
|
||||
with self.assertWarns(UserWarning):
|
||||
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')
|
||||
|
||||
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):
|
||||
"""The existance of a data reference box requires a ftbl box as well."""
|
||||
flag = 0
|
||||
|
|
@ -483,17 +553,17 @@ class TestJPX(unittest.TestCase):
|
|||
self.assertEqual(jpx.box[-1].box[0].data_reference, (3,))
|
||||
|
||||
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)
|
||||
self.assertEqual(j.box[16].box[1].box[0].box_id, 'nlst')
|
||||
self.assertEqual(type(j.box[16].box[1].box[0]),
|
||||
glymur.jp2box.NumberListBox)
|
||||
nlst = j.box[12].box[0].box[0]
|
||||
self.assertEqual(nlst.box_id, 'nlst')
|
||||
self.assertEqual(type(nlst), glymur.jp2box.NumberListBox)
|
||||
|
||||
# Two associations.
|
||||
self.assertEqual(len(j.box[16].box[1].box[0].associations), 2)
|
||||
self.assertEqual(len(nlst.associations), 2)
|
||||
|
||||
# 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
|
||||
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 JPEG2000SignatureBox
|
||||
|
||||
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
class TestXML(unittest.TestCase):
|
||||
|
|
@ -286,3 +287,40 @@ class TestBadButRecoverableXmlFile(unittest.TestCase):
|
|||
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 .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||
|
||||
from . import fixtures
|
||||
|
||||
# Doc tests should be run as well.
|
||||
def load_tests(loader, tests, ignore):
|
||||
|
|
@ -53,9 +53,7 @@ def load_tests(loader, tests, ignore):
|
|||
|
||||
|
||||
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):
|
||||
self.jp2file = glymur.data.nemo()
|
||||
|
|
@ -65,6 +63,24 @@ class TestJp2k(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
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):
|
||||
"""Verify that results of __repr__ are eval-able."""
|
||||
j = Jp2k(self.j2kfile)
|
||||
|
|
@ -247,19 +263,6 @@ class TestJp2k(unittest.TestCase):
|
|||
j2k = Jp2k(self.j2kfile)
|
||||
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):
|
||||
"""Verify that the list of boxes in a J2C/J2K file is present, but
|
||||
empty.
|
||||
|
|
@ -376,20 +379,18 @@ class TestJp2k(unittest.TestCase):
|
|||
creator_tool = xmp.get_property(libxmp.consts.XMP_NS_XMP, 'CreatorTool')
|
||||
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):
|
||||
"""Read JPX codestream when jp2-compatible."""
|
||||
# The file in question has multiple codestreams.
|
||||
jpx = Jp2k(self.jpxfile)
|
||||
data = jpx.read()
|
||||
if re.match(r"""1\.[0123]""", glymur.version.openjpeg_version):
|
||||
# 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))
|
||||
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):
|
||||
"""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):
|
||||
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__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -327,7 +327,10 @@ class TestSuite(unittest.TestCase):
|
|||
|
||||
def test_ETS_JP2_file1(self):
|
||||
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()
|
||||
self.assertEqual(jpdata.shape, (512, 768, 3))
|
||||
|
||||
|
|
@ -381,11 +384,7 @@ class TestSuite(unittest.TestCase):
|
|||
jfile = opj_data_file('input/conformance/file9.jp2')
|
||||
jp2k = Jp2k(jfile)
|
||||
jpdata = jp2k.read()
|
||||
if re.match(r"""1\.3""", glymur.version.openjpeg_version):
|
||||
# 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))
|
||||
self.assertEqual(jpdata.shape, (512, 768, 3))
|
||||
|
||||
def test_NR_DEC_Bretagne2_j2k_1_decode(self):
|
||||
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):
|
||||
# Stream too short, expected SOT.
|
||||
jfile = opj_data_file('input/nonregression/illegalcolortransform.j2k')
|
||||
Jp2k(jfile).read()
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
Jp2k(jfile).read()
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_NR_DEC_j2k32_j2k_15_decode(self):
|
||||
|
|
@ -3116,7 +3117,10 @@ class TestSuiteDump(unittest.TestCase):
|
|||
|
||||
def test_NR_file1_dump(self):
|
||||
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]
|
||||
self.assertEqual(ids, ['jP ', 'ftyp', 'xml ', 'jp2h', 'xml ',
|
||||
|
|
@ -3297,7 +3301,7 @@ class TestSuiteDump(unittest.TestCase):
|
|||
|
||||
def test_NR_file5_dump(self):
|
||||
# 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
|
||||
# profile and using the JPX-defined enumerated code for the ROMM-RGB
|
||||
# colourspace.
|
||||
|
|
@ -3305,49 +3309,37 @@ class TestSuiteDump(unittest.TestCase):
|
|||
jp2 = Jp2k(jfile)
|
||||
|
||||
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]
|
||||
self.assertEqual(ids, ['ihdr', 'colr', 'colr'])
|
||||
ids = [box.box_id for box in jp2.box[2].box]
|
||||
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||
|
||||
# Signature box. Check for corruption.
|
||||
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
||||
|
||||
# File type box.
|
||||
self.assertEqual(jp2.box[1].brand, 'jpx ')
|
||||
self.assertEqual(jp2.box[1].brand, 'jp2 ')
|
||||
self.assertEqual(jp2.box[1].minor_version, 0)
|
||||
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
|
||||
# Image header
|
||||
self.assertEqual(jp2.box[3].box[0].height, 512)
|
||||
self.assertEqual(jp2.box[3].box[0].width, 768)
|
||||
self.assertEqual(jp2.box[3].box[0].num_components, 3)
|
||||
self.assertEqual(jp2.box[3].box[0].signed, False)
|
||||
self.assertEqual(jp2.box[3].box[0].compression, 7) # wavelet
|
||||
self.assertEqual(jp2.box[3].box[0].colorspace_unknown, False)
|
||||
self.assertEqual(jp2.box[3].box[0].ip_provided, False)
|
||||
self.assertEqual(jp2.box[2].box[0].height, 512)
|
||||
self.assertEqual(jp2.box[2].box[0].width, 768)
|
||||
self.assertEqual(jp2.box[2].box[0].num_components, 3)
|
||||
self.assertEqual(jp2.box[2].box[0].signed, False)
|
||||
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
|
||||
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
|
||||
|
||||
# Jp2 Header
|
||||
# Colour specification
|
||||
self.assertEqual(jp2.box[3].box[1].method,
|
||||
self.assertEqual(jp2.box[2].box[1].method,
|
||||
glymur.core.RESTRICTED_ICC_PROFILE) # enumerated
|
||||
self.assertEqual(jp2.box[3].box[1].precedence, 0)
|
||||
self.assertEqual(jp2.box[3].box[1].approximation, 1) # JPX exact
|
||||
self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 546)
|
||||
self.assertIsNone(jp2.box[3].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)
|
||||
self.assertEqual(jp2.box[2].box[1].precedence, 0)
|
||||
self.assertEqual(jp2.box[2].box[1].approximation, 1) # JPX exact
|
||||
self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 546)
|
||||
self.assertIsNone(jp2.box[2].box[1].colorspace)
|
||||
|
||||
def test_NR_file6_dump(self):
|
||||
jfile = opj_data_file('input/conformance/file6.jp2')
|
||||
|
|
@ -3398,54 +3390,37 @@ class TestSuiteDump(unittest.TestCase):
|
|||
jp2 = Jp2k(jfile)
|
||||
|
||||
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]
|
||||
self.assertEqual(ids, ['ihdr', 'colr', 'colr'])
|
||||
ids = [box.box_id for box in jp2.box[2].box]
|
||||
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||
|
||||
# Signature box. Check for corruption.
|
||||
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
||||
|
||||
# File type box.
|
||||
self.assertEqual(jp2.box[1].brand, 'jpx ')
|
||||
self.assertEqual(jp2.box[1].brand, '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
|
||||
# Image header
|
||||
self.assertEqual(jp2.box[3].box[0].height, 640)
|
||||
self.assertEqual(jp2.box[3].box[0].width, 480)
|
||||
self.assertEqual(jp2.box[3].box[0].num_components, 3)
|
||||
self.assertEqual(jp2.box[3].box[0].bits_per_component, 16)
|
||||
self.assertEqual(jp2.box[3].box[0].signed, False)
|
||||
self.assertEqual(jp2.box[3].box[0].compression, 7) # wavelet
|
||||
self.assertEqual(jp2.box[3].box[0].colorspace_unknown, False)
|
||||
self.assertEqual(jp2.box[3].box[0].ip_provided, False)
|
||||
self.assertEqual(jp2.box[2].box[0].height, 640)
|
||||
self.assertEqual(jp2.box[2].box[0].width, 480)
|
||||
self.assertEqual(jp2.box[2].box[0].num_components, 3)
|
||||
self.assertEqual(jp2.box[2].box[0].bits_per_component, 16)
|
||||
self.assertEqual(jp2.box[2].box[0].signed, False)
|
||||
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
|
||||
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
|
||||
|
||||
# Jp2 Header
|
||||
# Colour specification
|
||||
self.assertEqual(jp2.box[3].box[1].method,
|
||||
self.assertEqual(jp2.box[2].box[1].method,
|
||||
glymur.core.RESTRICTED_ICC_PROFILE)
|
||||
self.assertEqual(jp2.box[3].box[1].precedence, 0)
|
||||
self.assertEqual(jp2.box[3].box[1].approximation, 1) # JPX exact
|
||||
self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 13332)
|
||||
self.assertIsNone(jp2.box[3].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)
|
||||
self.assertEqual(jp2.box[2].box[1].precedence, 0)
|
||||
self.assertEqual(jp2.box[2].box[1].approximation, 1)
|
||||
self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 13332)
|
||||
self.assertIsNone(jp2.box[2].box[1].colorspace)
|
||||
|
||||
def test_NR_file8_dump(self):
|
||||
# One 8-bit component in a gamma 1.8 space. The colourspace is
|
||||
|
|
@ -3472,7 +3447,7 @@ class TestSuiteDump(unittest.TestCase):
|
|||
# Image header
|
||||
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].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].signed, False)
|
||||
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||
|
|
@ -5507,7 +5482,7 @@ class TestSuiteDump(unittest.TestCase):
|
|||
jp2 = Jp2k(jfile)
|
||||
|
||||
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]
|
||||
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||
|
|
@ -5861,9 +5836,9 @@ class TestSuiteDump(unittest.TestCase):
|
|||
|
||||
# Jp2 Header
|
||||
# Component mapping box
|
||||
self.assertEqual(jp2.box[3].box[3].component_index, (0, 1, 2))
|
||||
self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 0))
|
||||
self.assertEqual(jp2.box[3].box[3].palette_index, (0, 0, 1))
|
||||
self.assertEqual(jp2.box[3].box[3].component_index, (0, 0, 0, 0))
|
||||
self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1, 1))
|
||||
self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2, 3))
|
||||
|
||||
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])
|
||||
|
||||
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')
|
||||
with warnings.catch_warnings():
|
||||
# brand is 'jp2 ', but has any icc profile.
|
||||
warnings.simplefilter("ignore")
|
||||
jp2 = Jp2k(jfile)
|
||||
if sys.hexversion > 0x03030000:
|
||||
with self.assertWarns(UserWarning):
|
||||
jp2 = Jp2k(jfile)
|
||||
else:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
jp2 = Jp2k(jfile)
|
||||
|
||||
ids = [box.box_id for box in jp2.box]
|
||||
lst = ['jP ', 'ftyp', 'rreq', 'jp2h',
|
||||
|
|
@ -6647,6 +6627,7 @@ class TestSuite2point1(unittest.TestCase):
|
|||
Jp2k(jfile).read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skip("Failing as of r2436")
|
||||
def test_NR_DEC_mem_b2ace68c_1381_jp2_34_decode(self):
|
||||
jfile = opj_data_file('input/nonregression/mem-b2ace68c-1381.jp2')
|
||||
with warnings.catch_warnings():
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ from glymur import Jp2k
|
|||
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,
|
||||
"OPJ_OPJ_DATA_ROOT environment variable not set")
|
||||
class TestSuiteNegative(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -12,25 +12,252 @@ import sys
|
|||
import tempfile
|
||||
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 OPJ_DATA_ROOT, opj_data_file
|
||||
from . import fixtures
|
||||
|
||||
from glymur import Jp2k
|
||||
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(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(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
class TestSuiteWrite(unittest.TestCase):
|
||||
"""Tests for writing with openjp2 backend.
|
||||
|
||||
These tests roughly correspond with those tests with similar names in the
|
||||
OpenJPEG test suite.
|
||||
These tests either roughly correspond with those tests with similar names
|
||||
in the OpenJPEG test suite or are closely associated.
|
||||
"""
|
||||
def setUp(self):
|
||||
pass
|
||||
|
|
@ -852,5 +1079,6 @@ class TestSuiteWrite(unittest.TestCase):
|
|||
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
|
||||
self.assertEqual(len(codestream.segment[2].spcod), 9)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ class TestPrinting(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
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.")
|
||||
def test_unknown_superbox(self):
|
||||
"""Verify that we can handle an unknown superbox."""
|
||||
|
|
@ -60,6 +68,9 @@ class TestPrinting(unittest.TestCase):
|
|||
# Add the header for an unknwon superbox.
|
||||
write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
|
||||
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)
|
||||
tfile.write(write_buffer)
|
||||
tfile.flush()
|
||||
|
|
@ -70,8 +81,7 @@ class TestPrinting(unittest.TestCase):
|
|||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||
print(jpx.box[-1])
|
||||
actual = fake_out.getvalue().strip()
|
||||
lines = ['Unknown Box (grp ) @ (695609, 20)',
|
||||
' Free Box (free) @ (695617, 12)']
|
||||
lines = ["Unknown Box (b'grp ') @ (1399071, 20)"]
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
|
@ -133,7 +143,6 @@ class TestPrinting(unittest.TestCase):
|
|||
lst = lst[1:]
|
||||
actual = '\n'.join(lst)
|
||||
expected = fixtures.nemo_dump_no_xml
|
||||
self.maxDiff = None
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_printoptions_short(self):
|
||||
|
|
@ -290,69 +299,6 @@ class TestPrinting(unittest.TestCase):
|
|||
expected = '\n'.join(lines)
|
||||
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):
|
||||
"""verify printing of eoc segment"""
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
|
|
@ -365,91 +311,6 @@ class TestPrinting(unittest.TestCase):
|
|||
expected = '\n'.join(lines)
|
||||
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):
|
||||
"""verify printing of qcc segment"""
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
|
|
@ -490,7 +351,7 @@ class TestPrinting(unittest.TestCase):
|
|||
actual = fake_out.getvalue().strip()
|
||||
|
||||
lines = ['SIZ marker segment @ (3233, 47)',
|
||||
' Profile: 2',
|
||||
' Profile: no profile',
|
||||
' Reference Grid Height, Width: (1456 x 2592)',
|
||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||
' Reference Tile Height, Width: (1456 x 2592)',
|
||||
|
|
@ -544,25 +405,6 @@ class TestPrinting(unittest.TestCase):
|
|||
expected = '\n'.join(lines)
|
||||
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):
|
||||
"""Verify the printing of a UUID/XMP box."""
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
|
|
@ -582,7 +424,7 @@ class TestPrinting(unittest.TestCase):
|
|||
lst = ['Codestream:',
|
||||
' SOC marker segment @ (3231, 0)',
|
||||
' SIZ marker segment @ (3233, 47)',
|
||||
' Profile: 2',
|
||||
' Profile: no profile',
|
||||
' Reference Grid Height, Width: (1456 x 2592)',
|
||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||
' Reference Tile Height, Width: (1456 x 2592)',
|
||||
|
|
@ -622,17 +464,6 @@ class TestPrinting(unittest.TestCase):
|
|||
expected = '\n'.join(lst)
|
||||
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,
|
||||
"Only trusting python3 for printing non-ascii chars")
|
||||
def test_xml_latin1(self):
|
||||
|
|
@ -690,100 +521,6 @@ class TestPrinting(unittest.TestCase):
|
|||
expected = '\n'.join(lines)
|
||||
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.")
|
||||
def test_less_common_boxes(self):
|
||||
"""verify uinf, ulst, url, res, resd, resc box printing"""
|
||||
|
|
@ -861,50 +598,6 @@ class TestPrinting(unittest.TestCase):
|
|||
expected = '\n'.join(lines)
|
||||
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,
|
||||
"Ordered dicts not printing well in 2.7")
|
||||
def test_exif_uuid(self):
|
||||
|
|
@ -946,5 +639,395 @@ class TestPrinting(unittest.TestCase):
|
|||
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__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ License: MIT
|
|||
"""
|
||||
|
||||
import sys
|
||||
import numpy as np
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
import lxml.etree
|
||||
import numpy as np
|
||||
|
||||
from .lib import openjpeg as opj
|
||||
from .lib import openjp2 as opj2
|
||||
|
||||
|
|
@ -48,10 +50,12 @@ OPENJPEG {openjpeg}
|
|||
Python {python}
|
||||
sys.platform {platform}
|
||||
sys.maxsize {maxsize}
|
||||
lxml {elxml}
|
||||
numpy {numpy}
|
||||
""".format(glymur=version,
|
||||
openjpeg=openjpeg_version,
|
||||
python=sys.version,
|
||||
platform=sys.platform,
|
||||
maxsize=sys.maxsize,
|
||||
elxml=lxml.etree.__version__,
|
||||
numpy=np.__version__)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue