Merge branch 'issue130' of https://github.com/quintusdias/glymur into issue130

Conflicts:
	glymur/jp2box.py
This commit is contained in:
jevans 2014-04-03 21:38:08 -04:00
commit 6ee48e3f91
28 changed files with 1975 additions and 1075 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.pyc

View file

@ -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:

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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
===================

View file

@ -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])

View file

@ -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}, "

View file

@ -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.

View file

@ -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

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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.

View file

@ -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))"""

View file

@ -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()

View file

@ -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()

View file

@ -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')

View file

@ -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."""

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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():

View file

@ -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):

View file

@ -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()

View file

@ -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()

View file

@ -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__)