Merge branch 'issue217' into devel
This commit is contained in:
commit
275324fce8
10 changed files with 399 additions and 83 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
"""
|
"""
|
||||||
Part of glymur.
|
Part of glymur.
|
||||||
"""
|
"""
|
||||||
|
from collections import OrderedDict
|
||||||
import pprint
|
import pprint
|
||||||
import re
|
import re
|
||||||
import struct
|
import struct
|
||||||
|
|
@ -10,12 +11,6 @@ import warnings
|
||||||
|
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
if sys.hexversion < 0x02070000:
|
|
||||||
# pylint: disable=F0401,E0611
|
|
||||||
from ordereddict import OrderedDict
|
|
||||||
else:
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
def xml(raw_data):
|
def xml(raw_data):
|
||||||
"""
|
"""
|
||||||
XMP data to be parsed as XML.
|
XMP data to be parsed as XML.
|
||||||
|
|
|
||||||
|
|
@ -188,15 +188,7 @@ class Codestream(object):
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
read_buffer = fptr.read(2)
|
read_buffer = fptr.read(2)
|
||||||
try:
|
|
||||||
self._marker_id, = struct.unpack('>H', read_buffer)
|
self._marker_id, = struct.unpack('>H', read_buffer)
|
||||||
except struct.error:
|
|
||||||
# Treat this as a warning.
|
|
||||||
msg = "Marker had length {0} instead of expected length of 2 "
|
|
||||||
msg += "bytes. Codestream parsing terminated."
|
|
||||||
warnings.warn(msg.format(len(read_buffer)))
|
|
||||||
break
|
|
||||||
|
|
||||||
self._offset = fptr.tell() - 2
|
self._offset = fptr.tell() - 2
|
||||||
|
|
||||||
if self._marker_id == 0xff90 and header_only:
|
if self._marker_id == 0xff90 and header_only:
|
||||||
|
|
|
||||||
|
|
@ -1011,7 +1011,7 @@ class ContiguousCodestreamBox(Jp2kBox):
|
||||||
if self._main_header is None:
|
if self._main_header is None:
|
||||||
if self._filename is not None:
|
if self._filename is not None:
|
||||||
with open(self._filename, 'rb') as fptr:
|
with open(self._filename, 'rb') as fptr:
|
||||||
fptr.seek(self._offset + 8)
|
fptr.seek(self.main_header_offset)
|
||||||
main_header = Codestream(fptr, self._length, header_only=True)
|
main_header = Codestream(fptr, self._length, header_only=True)
|
||||||
self._main_header = main_header
|
self._main_header = main_header
|
||||||
return self._main_header
|
return self._main_header
|
||||||
|
|
@ -1059,7 +1059,6 @@ class ContiguousCodestreamBox(Jp2kBox):
|
||||||
length=length, offset=offset)
|
length=length, offset=offset)
|
||||||
box._filename = fptr.name
|
box._filename = fptr.name
|
||||||
box._length = length
|
box._length = length
|
||||||
box._offset = offset
|
|
||||||
return box
|
return box
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1110,7 +1109,7 @@ class DataReferenceBox(Jp2kBox):
|
||||||
"""
|
"""
|
||||||
self._write_validate()
|
self._write_validate()
|
||||||
|
|
||||||
# Very similar to the say a superbox is written.
|
# Very similar to the way a superbox is written.
|
||||||
orig_pos = fptr.tell()
|
orig_pos = fptr.tell()
|
||||||
fptr.write(struct.pack('>I4s', 0, b'dtbl'))
|
fptr.write(struct.pack('>I4s', 0, b'dtbl'))
|
||||||
|
|
||||||
|
|
@ -1176,7 +1175,7 @@ class DataReferenceBox(Jp2kBox):
|
||||||
box = DataEntryURLBox.parse(box_fptr, 0, box_length)
|
box = DataEntryURLBox.parse(box_fptr, 0, box_length)
|
||||||
|
|
||||||
# Need to adjust the box start to that of the "real" file.
|
# Need to adjust the box start to that of the "real" file.
|
||||||
box.start = offset + box_offset
|
box.offset = offset + 8 + box_offset
|
||||||
data_entry_url_box_list.append(box)
|
data_entry_url_box_list.append(box)
|
||||||
|
|
||||||
# Point to the next embedded URL box.
|
# Point to the next embedded URL box.
|
||||||
|
|
@ -1341,7 +1340,10 @@ class FragmentListBox(Jp2kBox):
|
||||||
self._dispatch_validation_error(msg, writing=writing)
|
self._dispatch_validation_error(msg, writing=writing)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.FragmentListBox()"
|
msg = "glymur.jp2box.FragmentListBox({0}, {1}, {2})"
|
||||||
|
msg = msg.format(str(self.fragment_offset),
|
||||||
|
str(self.fragment_length),
|
||||||
|
str(self.data_reference))
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -1426,7 +1428,8 @@ class FragmentTableBox(Jp2kBox):
|
||||||
self.box = box if box is not None else []
|
self.box = box if box is not None else []
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.FragmentTableBox()"
|
msg = "glymur.jp2box.FragmentTableBox(box={0})"
|
||||||
|
msg = msg.format(None) if (len(self.box) == 0) else msg.format(self.box)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -1884,6 +1887,12 @@ class PaletteBox(Jp2kBox):
|
||||||
msg = "The length of the 'bits_per_component' and the 'signed' "
|
msg = "The length of the 'bits_per_component' and the 'signed' "
|
||||||
msg += "members must equal the number of columns of the palette."
|
msg += "members must equal the number of columns of the palette."
|
||||||
self._dispatch_validation_error(msg, writing=writing)
|
self._dispatch_validation_error(msg, writing=writing)
|
||||||
|
bps = self.bits_per_component
|
||||||
|
if writing and not all(b == bps[0] for b in bps):
|
||||||
|
# We don't support writing palettes with bit depths that are
|
||||||
|
# different.
|
||||||
|
msg = "Writing palettes with varying bit depths is not supported."
|
||||||
|
self._dispatch_validation_error(msg, writing=writing)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
|
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
|
||||||
|
|
@ -1925,7 +1934,6 @@ class PaletteBox(Jp2kBox):
|
||||||
fptr.write(write_buffer)
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
bps = self.bits_per_component
|
bps = self.bits_per_component
|
||||||
if all(b == bps[0] for b in bps):
|
|
||||||
# All components are the same. Writing is straightforward.
|
# All components are the same. Writing is straightforward.
|
||||||
if self.bits_per_component[0] <= 8:
|
if self.bits_per_component[0] <= 8:
|
||||||
write_buffer = memoryview(self.palette.astype(np.uint8))
|
write_buffer = memoryview(self.palette.astype(np.uint8))
|
||||||
|
|
@ -1934,17 +1942,6 @@ class PaletteBox(Jp2kBox):
|
||||||
elif self.bits_per_component[0] <= 32:
|
elif self.bits_per_component[0] <= 32:
|
||||||
write_buffer = memoryview(self.palette.astype(np.uint32))
|
write_buffer = memoryview(self.palette.astype(np.uint32))
|
||||||
fptr.write(write_buffer)
|
fptr.write(write_buffer)
|
||||||
else:
|
|
||||||
# Not all the components are the same. More general, but much rarer
|
|
||||||
# case. Does this even happen.
|
|
||||||
code_dict = {8: 'B', 16: 'H', 32: 'I'}
|
|
||||||
codes = ''
|
|
||||||
for width in bps:
|
|
||||||
codes += code_dict[width]
|
|
||||||
fmt = '>' + codes
|
|
||||||
for row in self.palette:
|
|
||||||
write_buffer = struct.pack(fmt, *row)
|
|
||||||
fptr.write(write_buffer)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -2635,18 +2632,19 @@ class NumberListBox(Jp2kBox):
|
||||||
msg += 'the rendered result'
|
msg += 'the rendered result'
|
||||||
elif (association >> 24) == 1:
|
elif (association >> 24) == 1:
|
||||||
idx = association & 0x00FFFFFF
|
idx = association & 0x00FFFFFF
|
||||||
msg += 'Codestream {0}'
|
msg += 'codestream {0}'
|
||||||
msg = msg.format(idx)
|
msg = msg.format(idx)
|
||||||
elif (association >> 24) == 2:
|
elif (association >> 24) == 2:
|
||||||
idx = association & 0x00FFFFFF
|
idx = association & 0x00FFFFFF
|
||||||
msg += 'Compositing Layer {0}'
|
msg += 'compositing layer {0}'
|
||||||
msg = msg.format(idx)
|
msg = msg.format(idx)
|
||||||
else:
|
else:
|
||||||
msg += 'unrecognized'
|
msg += 'unrecognized'
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = 'glymur.jp2box.NumberListBox()'
|
msg = 'glymur.jp2box.NumberListBox(associations={0})'
|
||||||
|
msg = msg.format(self.associations)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -547,13 +547,13 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
opj2.setup_encoder(codec, cparams, image)
|
opj2.setup_encoder(codec, cparams, image)
|
||||||
|
|
||||||
if _OPENJP2_IS_OFFICIAL_V2:
|
if re.match("2.0", version.openjpeg_version) is not None:
|
||||||
fptr = libc.fopen(self.filename, 'wb')
|
fptr = libc.fopen(self.filename, 'wb')
|
||||||
strm = opj2.stream_create_default_file_stream(fptr, False)
|
strm = opj2.stream_create_default_file_stream(fptr, False)
|
||||||
stack.callback(opj2.stream_destroy, strm)
|
stack.callback(opj2.stream_destroy, strm)
|
||||||
stack.callback(libc.fclose, fptr)
|
stack.callback(libc.fclose, fptr)
|
||||||
else:
|
else:
|
||||||
# This routine introduced in 2.0 devel series.
|
# Introduced in 2.1 devel series.
|
||||||
strm = opj2.stream_create_default_file_stream_v3(self.filename,
|
strm = opj2.stream_create_default_file_stream_v3(self.filename,
|
||||||
False)
|
False)
|
||||||
stack.callback(opj2.stream_destroy_v3, strm)
|
stack.callback(opj2.stream_destroy_v3, strm)
|
||||||
|
|
@ -1080,17 +1080,17 @@ class Jp2k(Jp2kBox):
|
||||||
layer=layer, tile=tile, area=area)
|
layer=layer, tile=tile, area=area)
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
if hasattr(opj2.OPENJP2,
|
if re.match("2.0", version.openjpeg_version):
|
||||||
'opj_stream_create_default_file_stream_v3'):
|
|
||||||
filename = self.filename
|
|
||||||
stream = opj2.stream_create_default_file_stream_v3(filename,
|
|
||||||
True)
|
|
||||||
stack.callback(opj2.stream_destroy_v3, stream)
|
|
||||||
else:
|
|
||||||
fptr = libc.fopen(self.filename, 'rb')
|
fptr = libc.fopen(self.filename, 'rb')
|
||||||
stack.callback(libc.fclose, fptr)
|
stack.callback(libc.fclose, fptr)
|
||||||
stream = opj2.stream_create_default_file_stream(fptr, True)
|
stream = opj2.stream_create_default_file_stream(fptr, True)
|
||||||
stack.callback(opj2.stream_destroy, stream)
|
stack.callback(opj2.stream_destroy, stream)
|
||||||
|
else:
|
||||||
|
# API change in 2.1+
|
||||||
|
filename = self.filename
|
||||||
|
stream = opj2.stream_create_default_file_stream_v3(filename,
|
||||||
|
True)
|
||||||
|
stack.callback(opj2.stream_destroy_v3, stream)
|
||||||
codec = opj2.create_decompress(self._codec_format)
|
codec = opj2.create_decompress(self._codec_format)
|
||||||
stack.callback(opj2.destroy_codec, codec)
|
stack.callback(opj2.destroy_codec, codec)
|
||||||
|
|
||||||
|
|
@ -1147,12 +1147,6 @@ class Jp2k(Jp2kBox):
|
||||||
Bitdepth: (8, 8, 8)
|
Bitdepth: (8, 8, 8)
|
||||||
Signed: (False, False, False)
|
Signed: (False, False, False)
|
||||||
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
|
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
|
||||||
|
|
||||||
Raises
|
|
||||||
------
|
|
||||||
IOError
|
|
||||||
If the file is JPX with more than one codestream and no JP2
|
|
||||||
compatibility is advertised.
|
|
||||||
"""
|
"""
|
||||||
with open(self.filename, 'rb') as fptr:
|
with open(self.filename, 'rb') as fptr:
|
||||||
if self._codec_format == opj2.CODEC_J2K:
|
if self._codec_format == opj2.CODEC_J2K:
|
||||||
|
|
@ -1161,11 +1155,6 @@ class Jp2k(Jp2kBox):
|
||||||
else:
|
else:
|
||||||
ftyp = self.box[1]
|
ftyp = self.box[1]
|
||||||
box = [x for x in self.box if x.box_id == 'jp2c']
|
box = [x for x in self.box if x.box_id == 'jp2c']
|
||||||
if len(box) > 1 and 'jp2 ' not in ftyp.compatibility_list:
|
|
||||||
msg = "If more than one codestream exists, JP2 "
|
|
||||||
msg += "compatibiltity must be advertised by the FileType "
|
|
||||||
msg += "box."
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
fptr.seek(box[0].offset)
|
fptr.seek(box[0].offset)
|
||||||
read_buffer = fptr.read(8)
|
read_buffer = fptr.read(8)
|
||||||
(box_length, _) = struct.unpack('>I4s', read_buffer)
|
(box_length, _) = struct.unpack('>I4s', read_buffer)
|
||||||
|
|
@ -1183,7 +1172,7 @@ class Jp2k(Jp2kBox):
|
||||||
return codestream
|
return codestream
|
||||||
|
|
||||||
|
|
||||||
def component2dtype(component):
|
def _component2dtype(component):
|
||||||
"""Take an OpenJPEG component structure and determine the numpy datatype.
|
"""Take an OpenJPEG component structure and determine the numpy datatype.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -1474,7 +1463,7 @@ def extract_image_cube(image):
|
||||||
"""
|
"""
|
||||||
ncomps = image.contents.numcomps
|
ncomps = image.contents.numcomps
|
||||||
component = image.contents.comps[0]
|
component = image.contents.comps[0]
|
||||||
dtype = component2dtype(component)
|
dtype = _component2dtype(component)
|
||||||
|
|
||||||
nrows = component.h
|
nrows = component.h
|
||||||
ncols = component.w
|
ncols = component.w
|
||||||
|
|
@ -1508,7 +1497,7 @@ def extract_image_bands(image):
|
||||||
for k in range(image.contents.numcomps):
|
for k in range(image.contents.numcomps):
|
||||||
component = image.contents.comps[k]
|
component = image.contents.comps[k]
|
||||||
|
|
||||||
dtype = component2dtype(component)
|
dtype = _component2dtype(component)
|
||||||
nrows = component.h
|
nrows = component.h
|
||||||
ncols = component.w
|
ncols = component.w
|
||||||
|
|
||||||
|
|
@ -1698,7 +1687,7 @@ def _validate_compression_params(img_array, cparams):
|
||||||
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
if _OPENJP2_IS_OFFICIAL_V2:
|
if re.match("2.0", version.openjpeg_version) is not None:
|
||||||
if (((img_array.ndim != 2) and
|
if (((img_array.ndim != 2) and
|
||||||
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
||||||
msg = "Writing images is restricted to single-channel "
|
msg = "Writing images is restricted to single-channel "
|
||||||
|
|
@ -1711,14 +1700,6 @@ def _validate_compression_params(img_array, cparams):
|
||||||
msg = "Only uint8 and uint16 images are currently supported."
|
msg = "Only uint8 and uint16 images are currently supported."
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
# Need to known if openjp2 library is the officially release v2.0.0 or not.
|
|
||||||
_OPENJP2_IS_OFFICIAL_V2 = False
|
|
||||||
if opj2.OPENJP2 is not None:
|
|
||||||
if opj2.version() == '2.0.0':
|
|
||||||
if not hasattr(opj2.OPENJP2,
|
|
||||||
'opj_stream_create_default_file_stream_v3'):
|
|
||||||
_OPENJP2_IS_OFFICIAL_V2 = True
|
|
||||||
|
|
||||||
_COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
|
_COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
|
||||||
'gray': opj2.CLRSPC_GRAY,
|
'gray': opj2.CLRSPC_GRAY,
|
||||||
'grey': opj2.CLRSPC_GRAY,
|
'grey': opj2.CLRSPC_GRAY,
|
||||||
|
|
|
||||||
|
|
@ -622,3 +622,57 @@ cinema2k_profile = """SIZ marker segment @ (2, 47)
|
||||||
Bitdepth: (12, 12, 12)
|
Bitdepth: (12, 12, 12)
|
||||||
Signed: (False, False, False)
|
Signed: (False, False, False)
|
||||||
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))"""
|
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))"""
|
||||||
|
|
||||||
|
jplh_color_group_box = r"""Compositing Layer Header Box (jplh) @ (314227, 31)
|
||||||
|
Colour Group Box (cgrp) @ (314235, 23)
|
||||||
|
Colour Specification Box (colr) @ (314243, 15)
|
||||||
|
Method: enumerated colorspace
|
||||||
|
Precedence: 0
|
||||||
|
Colorspace: sRGB"""
|
||||||
|
|
||||||
|
fragment_list_box = r"""Fragment List Box (flst) @ (-1, 0)
|
||||||
|
Offset 0: 89
|
||||||
|
Fragment Length 0: 1132288
|
||||||
|
Data Reference 0: 0"""
|
||||||
|
|
||||||
|
number_list_box = r"""Number List Box (nlst) @ (-1, 0)
|
||||||
|
Association[0]: the rendered result
|
||||||
|
Association[1]: codestream 0
|
||||||
|
Association[2]: compositing layer 0"""
|
||||||
|
|
||||||
|
|
||||||
|
goodstuff = r"""Codestream:
|
||||||
|
SOC marker segment @ (0, 0)
|
||||||
|
SIZ marker segment @ (2, 47)
|
||||||
|
Profile: no profile
|
||||||
|
Reference Grid Height, Width: (800 x 480)
|
||||||
|
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||||
|
Reference Tile Height, Width: (800 x 480)
|
||||||
|
Vertical, Horizontal Reference Tile Offset: (0 x 0)
|
||||||
|
Bitdepth: (8, 8, 8)
|
||||||
|
Signed: (False, False, False)
|
||||||
|
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
|
||||||
|
COD marker segment @ (51, 12)
|
||||||
|
Coding style:
|
||||||
|
Entropy coder, without partitions
|
||||||
|
SOP marker segments: False
|
||||||
|
EPH marker segments: False
|
||||||
|
Coding style parameters:
|
||||||
|
Progression order: LRCP
|
||||||
|
Number of layers: 1
|
||||||
|
Multiple component transformation usage: reversible
|
||||||
|
Number of resolutions: 6
|
||||||
|
Code block height, width: (64 x 64)
|
||||||
|
Wavelet transform: 5-3 reversible
|
||||||
|
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
|
||||||
|
QCD marker segment @ (65, 19)
|
||||||
|
Quantization style: no quantization, 2 guard bits
|
||||||
|
Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -543,6 +543,16 @@ class TestPaletteBox(unittest.TestCase):
|
||||||
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
||||||
signed=signed)
|
signed=signed)
|
||||||
|
|
||||||
|
def test_writing_with_different_bitdepths(self):
|
||||||
|
"""Bitdepths must be the same when writing."""
|
||||||
|
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint16)
|
||||||
|
bps = (8, 16, 8)
|
||||||
|
signed = (False, False, False)
|
||||||
|
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
|
||||||
|
signed=signed)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
pclr.write(tfile)
|
||||||
|
|
||||||
class TestAppend(unittest.TestCase):
|
class TestAppend(unittest.TestCase):
|
||||||
"""Tests for append method."""
|
"""Tests for append method."""
|
||||||
|
|
@ -733,6 +743,49 @@ class TestWrap(unittest.TestCase):
|
||||||
boxes = [box.box_id for box in jp2.box]
|
boxes = [box.box_id for box in jp2.box]
|
||||||
self.assertEqual(boxes, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
|
self.assertEqual(boxes, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
|
||||||
|
|
||||||
|
def test_wrap_jp2_Lzero(self):
|
||||||
|
"""Wrap jp2 with jp2c box length is zero"""
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||||
|
with open(self.jp2file, 'rb') as ifile:
|
||||||
|
tfile.write(ifile.read())
|
||||||
|
# Rewrite with codestream length as zero.
|
||||||
|
tfile.seek(3223)
|
||||||
|
tfile.write(struct.pack('>I', 0))
|
||||||
|
tfile.flush()
|
||||||
|
jp2 = Jp2k(tfile.name)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
|
||||||
|
jp2 = jp2.wrap(tfile2.name)
|
||||||
|
boxes = [box for box in jp2.box]
|
||||||
|
self.assertEqual(boxes[3].length, 1132296)
|
||||||
|
|
||||||
|
def test_wrap_jp2_Lone(self):
|
||||||
|
"""Wrap jp2 with jp2c box length is 1, implies Q field"""
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||||
|
with open(self.jp2file, 'rb') as ifile:
|
||||||
|
tfile.write(ifile.read(3223))
|
||||||
|
# Write new L, T, Q fields
|
||||||
|
tfile.write(struct.pack('>I4sQ', 1, b'jp2c', 1132296 + 8))
|
||||||
|
# skip over the old L, T fields
|
||||||
|
ifile.seek(3231)
|
||||||
|
tfile.write(ifile.read())
|
||||||
|
tfile.flush()
|
||||||
|
jp2 = Jp2k(tfile.name)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
|
||||||
|
jp2 = jp2.wrap(tfile2.name)
|
||||||
|
boxes = [box for box in jp2.box]
|
||||||
|
self.assertEqual(boxes[3].length, 1132296 + 8)
|
||||||
|
|
||||||
|
def test_wrap_compatibility_not_jp2(self):
|
||||||
|
"""File type compatibility must contain jp2"""
|
||||||
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
boxes = [box for box in jp2.box]
|
||||||
|
boxes[1].compatibility_list = ['jpx ']
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
def test_empty_jp2h(self):
|
def test_empty_jp2h(self):
|
||||||
"""JP2H box list cannot be empty."""
|
"""JP2H box list cannot be empty."""
|
||||||
jp2 = Jp2k(self.jp2file)
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
|
@ -992,6 +1045,59 @@ class TestRepr(unittest.TestCase):
|
||||||
self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox))
|
self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox))
|
||||||
self.assertEqual(newbox.signature, (13, 10, 135, 10))
|
self.assertEqual(newbox.signature, (13, 10, 135, 10))
|
||||||
|
|
||||||
|
def test_free(self):
|
||||||
|
"""Should be able to instantiate a free box"""
|
||||||
|
free = glymur.jp2box.FreeBox()
|
||||||
|
|
||||||
|
# Test the representation instantiation.
|
||||||
|
newbox = eval(repr(free))
|
||||||
|
self.assertTrue(isinstance(newbox, glymur.jp2box.FreeBox))
|
||||||
|
|
||||||
|
def test_nlst(self):
|
||||||
|
"""Should be able to instantiate a number list box"""
|
||||||
|
assn = (0, 1, 2)
|
||||||
|
nlst = glymur.jp2box.NumberListBox(assn)
|
||||||
|
|
||||||
|
# Test the representation instantiation.
|
||||||
|
newbox = eval(repr(nlst))
|
||||||
|
self.assertTrue(isinstance(newbox, glymur.jp2box.NumberListBox))
|
||||||
|
self.assertEqual(newbox.associations, (0, 1, 2))
|
||||||
|
|
||||||
|
def test_ftbl(self):
|
||||||
|
"""Should be able to instantiate a fragment table box"""
|
||||||
|
ftbl = glymur.jp2box.FragmentTableBox()
|
||||||
|
|
||||||
|
# Test the representation instantiation.
|
||||||
|
newbox = eval(repr(ftbl))
|
||||||
|
self.assertTrue(isinstance(newbox, glymur.jp2box.FragmentTableBox))
|
||||||
|
|
||||||
|
def test_dref(self):
|
||||||
|
"""Should be able to instantiate a data reference box"""
|
||||||
|
dref = glymur.jp2box.DataReferenceBox()
|
||||||
|
|
||||||
|
# Test the representation instantiation.
|
||||||
|
newbox = eval(repr(dref))
|
||||||
|
self.assertTrue(isinstance(newbox, glymur.jp2box.DataReferenceBox))
|
||||||
|
|
||||||
|
def test_flst(self):
|
||||||
|
"""Should be able to instantiate a fragment list box"""
|
||||||
|
flst = glymur.jp2box.FragmentListBox([89], [1132288], [0])
|
||||||
|
|
||||||
|
# Test the representation instantiation.
|
||||||
|
newbox = eval(repr(flst))
|
||||||
|
self.assertTrue(isinstance(newbox, glymur.jp2box.FragmentListBox))
|
||||||
|
self.assertEqual(newbox.fragment_offset, [89])
|
||||||
|
self.assertEqual(newbox.fragment_length, [1132288])
|
||||||
|
self.assertEqual(newbox.data_reference, [0])
|
||||||
|
|
||||||
|
def test_default_cgrp(self):
|
||||||
|
"""Should be able to instantiate a color group box"""
|
||||||
|
cgrp = glymur.jp2box.ColourGroupBox()
|
||||||
|
|
||||||
|
# Test the representation instantiation.
|
||||||
|
newbox = eval(repr(cgrp))
|
||||||
|
self.assertTrue(isinstance(newbox, glymur.jp2box.ColourGroupBox))
|
||||||
|
|
||||||
def test_default_ftyp(self):
|
def test_default_ftyp(self):
|
||||||
"""Should be able to instantiate a FileTypeBox"""
|
"""Should be able to instantiate a FileTypeBox"""
|
||||||
ftyp = glymur.jp2box.FileTypeBox()
|
ftyp = glymur.jp2box.FileTypeBox()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
Test suite specifically targeting JPX box layout.
|
Test suite specifically targeting JPX box layout.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ctypes
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -52,6 +53,7 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
tfile1.write(fptr.read())
|
tfile1.write(fptr.read())
|
||||||
tfile1.flush()
|
tfile1.flush()
|
||||||
jp2_1 = Jp2k(tfile1.name)
|
jp2_1 = Jp2k(tfile1.name)
|
||||||
|
jp2h = jp2_1.box[2]
|
||||||
|
|
||||||
jp2c = [box for box in jp2_1.box if box.box_id == 'jp2c'][0]
|
jp2c = [box for box in jp2_1.box if box.box_id == 'jp2c'][0]
|
||||||
|
|
||||||
|
|
@ -61,12 +63,13 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
clen = []
|
clen = []
|
||||||
dr_idx = []
|
dr_idx = []
|
||||||
|
|
||||||
coff.append(jp2c.offset + 8)
|
coff.append(jp2c.main_header_offset)
|
||||||
clen.append(jp2c.length - (coff[0] - jp2c.offset))
|
clen.append(jp2c.length - (coff[0] - jp2c.offset))
|
||||||
dr_idx.append(1)
|
dr_idx.append(1)
|
||||||
|
|
||||||
# Make the url box for this codestream.
|
# Make the url box for this codestream.
|
||||||
url1 = DataEntryURLBox(0, [0, 0, 0], 'file://' + tfile1.name)
|
url1 = DataEntryURLBox(0, [0, 0, 0], 'file://' + tfile1.name)
|
||||||
|
url1_name_len = len(url1.url) + 1
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
|
||||||
|
|
||||||
|
|
@ -74,7 +77,7 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
jp2_2 = j2k.wrap(tfile2.name)
|
jp2_2 = j2k.wrap(tfile2.name)
|
||||||
|
|
||||||
jp2c = [box for box in jp2_2.box if box.box_id == 'jp2c'][0]
|
jp2c = [box for box in jp2_2.box if box.box_id == 'jp2c'][0]
|
||||||
coff.append(jp2c.offset + 8)
|
coff.append(jp2c.main_header_offset)
|
||||||
clen.append(jp2c.length - (coff[0] - jp2c.offset))
|
clen.append(jp2c.length - (coff[0] - jp2c.offset))
|
||||||
dr_idx.append(2)
|
dr_idx.append(2)
|
||||||
|
|
||||||
|
|
@ -85,7 +88,7 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
FileTypeBox(brand='jpx ',
|
FileTypeBox(brand='jpx ',
|
||||||
compatibility_list=['jpx ',
|
compatibility_list=['jpx ',
|
||||||
'jp2 ', 'jpxb']),
|
'jp2 ', 'jpxb']),
|
||||||
jp2_1.box[2]]
|
jp2h]
|
||||||
with tempfile.NamedTemporaryFile(suffix='.jpx') as tjpx:
|
with tempfile.NamedTemporaryFile(suffix='.jpx') as tjpx:
|
||||||
for box in boxes:
|
for box in boxes:
|
||||||
box.write(tjpx)
|
box.write(tjpx)
|
||||||
|
|
@ -99,6 +102,15 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
dtbl.write(tjpx)
|
dtbl.write(tjpx)
|
||||||
tjpx.flush()
|
tjpx.flush()
|
||||||
|
|
||||||
|
jpx_no_jp2c = Jp2k(tjpx.name)
|
||||||
|
jpx_boxes = [box.box_id for box in jpx_no_jp2c.box]
|
||||||
|
self.assertEqual(jpx_boxes, ['jP ', 'ftyp', 'jp2h',
|
||||||
|
'ftbl', 'dtbl'])
|
||||||
|
self.assertEqual(jpx_no_jp2c.box[4].DR[0].offset, 141)
|
||||||
|
|
||||||
|
offset = 141 + 8 + 4 + url1_name_len
|
||||||
|
self.assertEqual(jpx_no_jp2c.box[4].DR[1].offset, offset)
|
||||||
|
|
||||||
def test_jp2_with_jpx_box(self):
|
def test_jp2_with_jpx_box(self):
|
||||||
"""If the brand is jp2, then no jpx boxes are allowed."""
|
"""If the brand is jp2, then no jpx boxes are allowed."""
|
||||||
jp2 = Jp2k(self.jp2file)
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
|
@ -154,8 +166,8 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
self.assertEqual(jpx.box[-1].box[0].box_id, 'colr')
|
self.assertEqual(jpx.box[-1].box[0].box_id, 'colr')
|
||||||
self.assertEqual(jpx.box[-1].box[1].box_id, 'colr')
|
self.assertEqual(jpx.box[-1].box[1].box_id, 'colr')
|
||||||
|
|
||||||
def test_cgrp_neg(self):
|
def test_label_neg(self):
|
||||||
"""Can't write a cgrp with anything but colr sub boxes"""
|
"""Can't write a label box embedded in any old box."""
|
||||||
jp2 = Jp2k(self.jp2file)
|
jp2 = Jp2k(self.jp2file)
|
||||||
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
|
||||||
|
|
||||||
|
|
@ -173,6 +185,26 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
jpx = jp2.wrap(tfile.name, boxes=boxes)
|
jpx = jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
the_xml = ET.fromstring('<?xml version="1.0"?><data>0</data>')
|
||||||
|
xmlb = glymur.jp2box.XMLBox(xml=the_xml)
|
||||||
|
box = [xmlb]
|
||||||
|
|
||||||
|
cgrp = glymur.jp2box.ColourGroupBox(box=box)
|
||||||
|
boxes.append(cgrp)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
jpx = jp2.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
def test_ftbl(self):
|
def test_ftbl(self):
|
||||||
"""Write a fragment table box."""
|
"""Write a fragment table box."""
|
||||||
# Add a negative test where offset < 0
|
# Add a negative test where offset < 0
|
||||||
|
|
@ -459,7 +491,7 @@ class TestJPX(unittest.TestCase):
|
||||||
self.assertEqual(jpx.box[2].box_id, 'rreq')
|
self.assertEqual(jpx.box[2].box_id, 'rreq')
|
||||||
self.assertEqual(type(jpx.box[2]),
|
self.assertEqual(type(jpx.box[2]),
|
||||||
glymur.jp2box.ReaderRequirementsBox)
|
glymur.jp2box.ReaderRequirementsBox)
|
||||||
self.assertEqual(jpx.box[2].standard_flag,
|
self.asserwrite_buffertEqual(jpx.box[2].standard_flag,
|
||||||
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
|
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
|
||||||
|
|
||||||
@unittest.skip("Requires unnecessarily complicated code")
|
@unittest.skip("Requires unnecessarily complicated code")
|
||||||
|
|
@ -552,6 +584,52 @@ class TestJPX(unittest.TestCase):
|
||||||
self.assertEqual(jpx.box[-1].box[0].fragment_length, (170246,))
|
self.assertEqual(jpx.box[-1].box[0].fragment_length, (170246,))
|
||||||
self.assertEqual(jpx.box[-1].box[0].data_reference, (3,))
|
self.assertEqual(jpx.box[-1].box[0].data_reference, (3,))
|
||||||
|
|
||||||
|
def test_rreq3(self):
|
||||||
|
"""Verify that we can read a rreq box with mask length 3 bytes"""
|
||||||
|
rreq_buffer = ctypes.create_string_buffer(74)
|
||||||
|
struct.pack_into('>I4s', rreq_buffer, 0, 74, b'rreq')
|
||||||
|
|
||||||
|
# mask length
|
||||||
|
struct.pack_into('>B', rreq_buffer, 8, 3)
|
||||||
|
|
||||||
|
# fuam, dcm. 6 bytes, two sets of 3.
|
||||||
|
lst = (255, 224, 0, 0, 31, 252)
|
||||||
|
struct.pack_into('>BBBBBB', rreq_buffer, 9, *lst)
|
||||||
|
|
||||||
|
# number of standard features: 11
|
||||||
|
struct.pack_into('>H', rreq_buffer, 15, 11)
|
||||||
|
|
||||||
|
standard_flags = [5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20]
|
||||||
|
standard_masks = [8388608, 4194304, 2097152, 1048576, 524288, 262144,
|
||||||
|
131072, 65536, 32768, 16384, 8192]
|
||||||
|
for j in range(len(standard_flags)):
|
||||||
|
mask = (standard_masks[j] >> 16,
|
||||||
|
standard_masks[j] & 0x0000ffff>> 8,
|
||||||
|
standard_masks[j] & 0x000000ff)
|
||||||
|
struct.pack_into('>HBBB', rreq_buffer, 17 + j * 5,
|
||||||
|
standard_flags[j], *mask)
|
||||||
|
|
||||||
|
# num vendor features: 0
|
||||||
|
struct.pack_into('>H', rreq_buffer, 72, 0)
|
||||||
|
|
||||||
|
# Ok, done with the box, we can now insert it into the jpx file after
|
||||||
|
# the ftyp box.
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jpx") as ofile:
|
||||||
|
with open(self.jpxfile, 'rb') as ifile:
|
||||||
|
ofile.write(ifile.read(40))
|
||||||
|
ofile.write(rreq_buffer)
|
||||||
|
ofile.write(ifile.read())
|
||||||
|
ofile.flush()
|
||||||
|
|
||||||
|
jpx = Jp2k(ofile.name)
|
||||||
|
|
||||||
|
self.assertEqual(jpx.box[2].box_id, 'rreq')
|
||||||
|
self.assertEqual(type(jpx.box[2]),
|
||||||
|
glymur.jp2box.ReaderRequirementsBox)
|
||||||
|
self.assertEqual(jpx.box[2].standard_flag,
|
||||||
|
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
|
||||||
|
|
||||||
|
|
||||||
def test_nlst(self):
|
def test_nlst(self):
|
||||||
"""Verify that we can handle a number list box."""
|
"""Verify that we can handle a number list box."""
|
||||||
j = Jp2k(self.jpxfile)
|
j = Jp2k(self.jpxfile)
|
||||||
|
|
|
||||||
|
|
@ -779,6 +779,17 @@ class TestParsing(unittest.TestCase):
|
||||||
with self.assertWarns(UserWarning):
|
with self.assertWarns(UserWarning):
|
||||||
jp2 = Jp2k(filename)
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
@unittest.skip("blah")
|
||||||
|
def test_main_header(self):
|
||||||
|
"""Verify that the main header is not loaded when parsing turned off."""
|
||||||
|
# The hidden _main_header attribute should show up after accessing it.
|
||||||
|
glymur.set_parseoptions(codestream=False)
|
||||||
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
jp2c = jp2.box[4]
|
||||||
|
self.assertIsNone(jp2c._main_header)
|
||||||
|
main_header = jp2c.main_header
|
||||||
|
self.assertIsNotNone(jp2c._main_header)
|
||||||
|
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
class TestJp2kOpjDataRoot(unittest.TestCase):
|
class TestJp2kOpjDataRoot(unittest.TestCase):
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@ import unittest
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
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 OPJ_DATA_ROOT, opj_data_file, read_image
|
from .fixtures import OPJ_DATA_ROOT, opj_data_file, read_image
|
||||||
from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
||||||
|
|
||||||
|
|
@ -35,6 +42,21 @@ class TestSuiteNegative(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@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.")
|
||||||
|
def test_cinema2K_bad_frame_rate(self):
|
||||||
|
"""Cinema2k frame rate must be either 24 or 48."""
|
||||||
|
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=36)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
|
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
def test_psnr_with_cratios(self):
|
def test_psnr_with_cratios(self):
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,18 @@ class TestPrinting(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_codestream(self):
|
||||||
|
"""Should be able to print a raw codestream."""
|
||||||
|
j = glymur.Jp2k(self.j2kfile)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(j)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
# Remove the file line, as that is filesystem-dependent.
|
||||||
|
lines = actual.split('\n')
|
||||||
|
actual = '\n'.join(lines[1:])
|
||||||
|
|
||||||
|
self.assertEqual(actual, fixtures.codestream)
|
||||||
|
|
||||||
def test_version_info(self):
|
def test_version_info(self):
|
||||||
"""Should be able to print(glymur.version.info)"""
|
"""Should be able to print(glymur.version.info)"""
|
||||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
|
@ -598,6 +610,63 @@ class TestPrinting(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_flst(self):
|
||||||
|
"""Verify printing of fragment list box."""
|
||||||
|
flst = glymur.jp2box.FragmentListBox([89], [1132288], [0])
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(flst)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.fragment_list_box)
|
||||||
|
|
||||||
|
def test_dref(self):
|
||||||
|
"""Verify printing of data reference box."""
|
||||||
|
dref = glymur.jp2box.DataReferenceBox()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(dref)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, 'Data Reference Box (dtbl) @ (-1, 0)')
|
||||||
|
|
||||||
|
def test_jplh_cgrp(self):
|
||||||
|
"""Verify printing of compositing layer header box, color group box."""
|
||||||
|
jpx = glymur.Jp2k(self.jpxfile)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jpx.box[7])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.jplh_color_group_box)
|
||||||
|
|
||||||
|
def test_free(self):
|
||||||
|
"""Verify printing of Free box."""
|
||||||
|
free = glymur.jp2box.FreeBox()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(free)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, 'Free Box (free) @ (-1, 0)')
|
||||||
|
|
||||||
|
def test_nlst(self):
|
||||||
|
"""Verify printing of number list box."""
|
||||||
|
assn = (0, 16777216, 33554432)
|
||||||
|
nlst = glymur.jp2box.NumberListBox(assn)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(nlst)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.number_list_box)
|
||||||
|
|
||||||
|
def test_ftbl(self):
|
||||||
|
"""Verify printing of fragment table box."""
|
||||||
|
ftbl = glymur.jp2box.FragmentTableBox()
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(ftbl)
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, 'Fragment Table Box (ftbl) @ (-1, 0)')
|
||||||
|
|
||||||
|
def test_jpch(self):
|
||||||
|
"""Verify printing of JPCH box."""
|
||||||
|
jpx = glymur.Jp2k(self.jpxfile)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jpx.box[3])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, 'Codestream Header Box (jpch) @ (887, 8)')
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000,
|
@unittest.skipIf(sys.hexversion < 0x03000000,
|
||||||
"Ordered dicts not printing well in 2.7")
|
"Ordered dicts not printing well in 2.7")
|
||||||
def test_exif_uuid(self):
|
def test_exif_uuid(self):
|
||||||
|
|
@ -893,6 +962,17 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_componentmapping_box_alpha(self):
|
||||||
|
"""Verify __repr__ method on cmap box."""
|
||||||
|
cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0),
|
||||||
|
mapping_type=(1, 1, 1),
|
||||||
|
palette_index=(0, 1, 2))
|
||||||
|
newbox = eval(repr(cmap))
|
||||||
|
self.assertEqual(newbox.box_id, 'cmap')
|
||||||
|
self.assertEqual(newbox.component_index, (0, 0, 0))
|
||||||
|
self.assertEqual(newbox.mapping_type, (1, 1, 1))
|
||||||
|
self.assertEqual(newbox.palette_index, (0, 1, 2))
|
||||||
|
|
||||||
def test_palette7(self):
|
def test_palette7(self):
|
||||||
"""verify printing of pclr box"""
|
"""verify printing of pclr box"""
|
||||||
filename = opj_data_file('input/conformance/file9.jp2')
|
filename = opj_data_file('input/conformance/file9.jp2')
|
||||||
|
|
@ -905,7 +985,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
|
||||||
expected = '\n'.join(lines)
|
expected = '\n'.join(lines)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
@unittest.skip("file7 no longer has a rreq")
|
|
||||||
def test_rreq(self):
|
def test_rreq(self):
|
||||||
"""verify printing of reader requirements box"""
|
"""verify printing of reader requirements box"""
|
||||||
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
filename = opj_data_file('input/nonregression/text_GBR.jp2')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue