merge branch 'issue311' into devel

This commit is contained in:
John Evans 2015-01-07 08:20:51 -05:00
commit bd3f491e20
11 changed files with 355 additions and 116 deletions

View file

@ -6,5 +6,7 @@ Changes in 0.8.0
================= =================
* Simplified writing images by moving data and options into the * Simplified writing images by moving data and options into the
constructor. This is backwards-incompatible with 0.7.x. constructor.
* The main_header attribute of the ContiguousCodestream class is now called
codestream.
* Deprecated :py:meth:`read` method in favor of array-style slicing. * Deprecated :py:meth:`read` method in favor of array-style slicing.

View file

@ -5,7 +5,7 @@ import argparse
import os import os
import warnings import warnings
from . import Jp2k, set_printoptions, lib from . import Jp2k, set_printoptions, set_parseoptions, lib
def main(): def main():
@ -13,23 +13,25 @@ def main():
Entry point for console script jp2dump. Entry point for console script jp2dump.
""" """
description = 'Print JPEG2000 metadata.' kwargs = {'description': 'Print JPEG2000 metadata.',
parser = argparse.ArgumentParser(description=description) 'formatter_class': argparse.ArgumentDefaultsHelpFormatter}
parser = argparse.ArgumentParser(**kwargs)
parser.add_argument('-x', '--noxml', parser.add_argument('-x', '--noxml',
help='Suppress XML.', help='suppress XML',
action='store_true') action='store_true')
parser.add_argument('-s', '--short', parser.add_argument('-s', '--short',
help='Only print box id, offset, and length.', help='only print box id, offset, and length',
action='store_true') action='store_true')
chelp = 'Level of codestream information. 0 suppressed all details, ' chelp = 'Level of codestream information. 0 suppresses all details, '
chelp += '1 prints headers, 2 prints the full codestream' chelp += '1 prints the main header, 2 prints the full codestream.'
parser.add_argument('-c', '--codestream', parser.add_argument('-c', '--codestream',
help=chelp, help=chelp,
metavar='LEVEL',
nargs=1, nargs=1,
type=int, type=int,
default=[0]) default=[1])
parser.add_argument('filename') parser.add_argument('filename')
@ -45,11 +47,8 @@ def main():
if codestream_level == 0: if codestream_level == 0:
set_printoptions(codestream=False) set_printoptions(codestream=False)
print_full_codestream = False elif codestream_level == 2:
elif codestream_level == 1: set_parseoptions(full_codestream=True)
print_full_codestream = False
else:
print_full_codestream = True
filename = args.filename filename = args.filename
@ -58,11 +57,14 @@ def main():
# JP2 metadata can be extensive, so don't print any warnings until we # JP2 metadata can be extensive, so don't print any warnings until we
# are done with the metadata. # are done with the metadata.
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
if (((jp2._codec_format == lib.openjp2.CODEC_J2K) and if jp2._codec_format == lib.openjp2.CODEC_J2K:
(codestream_level == 0))): if codestream_level == 0:
print('File: {0}'.format(os.path.basename(filename))) print('File: {0}'.format(os.path.basename(filename)))
elif print_full_codestream: elif codestream_level == 1:
print(jp2.get_codestream(header_only=False)) print(jp2)
elif codestream_level == 2:
print('File: {0}'.format(os.path.basename(filename)))
print(jp2.get_codestream(header_only=False))
else: else:
print(jp2) print(jp2)

View file

@ -1001,18 +1001,19 @@ class ContiguousCodestreamBox(Jp2kBox):
offset of the box from the start of the file. offset of the box from the start of the file.
longname : str longname : str
more verbose description of the box. more verbose description of the box.
main_header : Codestream object codestream : Codestream object
contains list of main header marker/segments Contains list of codestream marker/segments. By default, only the main
header is retrieved.
main_header_offset : int main_header_offset : int
offset of main header from start of file offset of main header from start of file
""" """
box_id = 'jp2c' box_id = 'jp2c'
longname = 'Contiguous Codestream' longname = 'Contiguous Codestream'
def __init__(self, main_header=None, main_header_offset=None, length=0, def __init__(self, codestream=None, main_header_offset=None, length=0,
offset=-1): offset=-1):
Jp2kBox.__init__(self) Jp2kBox.__init__(self)
self._main_header = main_header self._codestream = codestream
self.length = length self.length = length
self.offset = offset self.offset = offset
self.main_header_offset = main_header_offset self.main_header_offset = main_header_offset
@ -1021,20 +1022,23 @@ class ContiguousCodestreamBox(Jp2kBox):
self._filename = None self._filename = None
@property @property
def main_header(self): def codestream(self):
if self._main_header is None: if _parseoptions['full_codestream'] is True:
header_only = False
else:
header_only = True
if self._codestream 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.main_header_offset) fptr.seek(self.main_header_offset)
main_header = Codestream(fptr, codestream = Codestream(fptr, self._length,
self._length, header_only=header_only)
header_only=True) self._codestream = codestream
self._main_header = main_header return self._codestream
return self._main_header
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.ContiguousCodeStreamBox(main_header={0})" msg = "glymur.jp2box.ContiguousCodeStreamBox(codestream={0})"
return msg.format(repr(self.main_header)) return msg.format(repr(self.codestream))
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
@ -1043,9 +1047,8 @@ class ContiguousCodestreamBox(Jp2kBox):
if _printoptions['codestream'] is False: if _printoptions['codestream'] is False:
return msg return msg
msg += '\n Main header:' for segment in self.codestream.segment:
for segment in self.main_header.segment: msg += '\n' + self._indent(str(segment), indent_level=4)
msg += '\n' + self._indent(str(segment), indent_level=8)
return msg return msg
@ -1067,11 +1070,11 @@ class ContiguousCodestreamBox(Jp2kBox):
ContiguousCodestreamBox instance ContiguousCodestreamBox instance
""" """
main_header_offset = fptr.tell() main_header_offset = fptr.tell()
if _parseoptions['codestream'] is True: if _parseoptions['full_codestream'] is True:
main_header = Codestream(fptr, length, header_only=True) codestream = Codestream(fptr, length, header_only=False)
else: else:
main_header = None codestream = None
box = cls(main_header, main_header_offset=main_header_offset, box = cls(codestream, main_header_offset=main_header_offset,
length=length, offset=offset) length=length, offset=offset)
box._filename = fptr.name box._filename = fptr.name
box._length = length box._length = length
@ -1320,7 +1323,7 @@ class FileTypeBox(Jp2kBox):
if sys.hexversion >= 0x03000000: if sys.hexversion >= 0x03000000:
try: try:
entry = entry.decode('utf-8') entry = entry.decode('utf-8')
except UnicodeDecodeError as err: except UnicodeDecodeError:
# The entry is invalid, but we've got code to catch this # The entry is invalid, but we've got code to catch this
# later on. # later on.
pass pass
@ -3314,19 +3317,20 @@ _BOX_WITH_ID = {
b'uuid': UUIDBox, b'uuid': UUIDBox,
b'xml ': XMLBox} b'xml ': XMLBox}
_parseoptions = {'codestream': True} _parseoptions = {'full_codestream': False}
def set_parseoptions(codestream=True): def set_parseoptions(full_codestream=True):
"""Set parsing options. """Set parsing options.
These options determine the way JPEG 2000 boxes are parsed. These options determine the way JPEG 2000 boxes are parsed.
Parameters Parameters
---------- ----------
codestream : bool, defaults to True full_codestream : bool, defaults to True
When False, the codestream header is only parsed when accessed. This When False, only the codestream header is parsed for metadata. This
can results in faster JP2/JPX parsing. can results in faster JP2/JPX parsing. When True, the entire
codestream is parsed for metadata.
See also See also
-------- --------
@ -3337,9 +3341,9 @@ def set_parseoptions(codestream=True):
To put back the default options, you can use: To put back the default options, you can use:
>>> import glymur >>> import glymur
>>> glymur.set_parseoptions(codestream=True) >>> glymur.set_parseoptions(full_codestream=True)
""" """
_parseoptions['codestream'] = codestream _parseoptions['full_codestream'] = full_codestream
def get_parseoptions(): def get_parseoptions():
@ -3378,7 +3382,9 @@ def set_printoptions(**kwargs):
When False, printing of the XML contents of any XML boxes or UUID XMP When False, printing of the XML contents of any XML boxes or UUID XMP
boxes is suppressed. boxes is suppressed.
codestream : bool, optional codestream : bool, optional
When False, printing of the codestream contents is suppressed. When False, only the codestream header is printed. When True, the
entire codestream is printed. This option has no effect when the
'short' option is set to True.
See also See also
-------- --------

View file

@ -2,6 +2,7 @@
Test fixtures common to more than one test point. Test fixtures common to more than one test point.
""" """
import os import os
import platform
import re import re
import sys import sys
import textwrap import textwrap
@ -33,6 +34,14 @@ elif re.match('1.[0-6]', six.__version__) is not None:
WARNING_INFRASTRUCTURE_ISSUE = True WARNING_INFRASTRUCTURE_ISSUE = True
msg = "Cannot run test with version {0} of python-six" msg = "Cannot run test with version {0} of python-six"
WARNING_INFRASTRUCTURE_MSG = msg.format(six.__version__) WARNING_INFRASTRUCTURE_MSG = msg.format(six.__version__)
elif ((re.match('1.8', six.__version__) is not None) and
(sys.platform.startswith('linux')) and
(platform.linux_distribution() == ('LinuxMint', '17', 'qiana'))):
WARNING_INFRASTRUCTURE_ISSUE = True
linux_distribution = platform.linux_distribution()
msg = "Cannot run test with version {0} of python-six on {1}"
WARNING_INFRASTRUCTURE_MSG = msg.format(six.__version__,
platform.linux_distribution)
# Cannot reopen a named temporary file in windows. # Cannot reopen a named temporary file in windows.
WINDOWS_TMP_FILE_MSG = "cannot use NamedTemporaryFile like this in windows" WINDOWS_TMP_FILE_MSG = "cannot use NamedTemporaryFile like this in windows"
@ -709,6 +718,190 @@ UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
nemo = """JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Size: [1456 2592 3]
Bitdepth: 8
Signed: False
Compression: wavelet
Colorspace Unknown: False
Colour Specification Box (colr) @ (62, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data:
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<ns0:xmpmeta xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ns0="adobe:ns:meta/" xmlns:ns2="http://ns.adobe.com/xap/1.0/" xmlns:ns3="http://ns.adobe.com/tiff/1.0/" xmlns:ns4="http://ns.adobe.com/exif/1.0/" xmlns:ns5="http://ns.adobe.com/photoshop/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ns0:xmptk="Exempi + XMP Core 5.1.2">
<rdf:RDF>
<rdf:Description rdf:about="">
<ns2:CreatorTool>Google</ns2:CreatorTool>
<ns2:CreateDate>2013-02-09T14:47:53</ns2:CreateDate>
</rdf:Description>
<rdf:Description rdf:about="">
<ns3:YCbCrPositioning>1</ns3:YCbCrPositioning>
<ns3:XResolution>72/1</ns3:XResolution>
<ns3:YResolution>72/1</ns3:YResolution>
<ns3:ResolutionUnit>2</ns3:ResolutionUnit>
<ns3:Make>HTC</ns3:Make>
<ns3:Model>HTC Glacier</ns3:Model>
<ns3:ImageWidth>2592</ns3:ImageWidth>
<ns3:ImageLength>1456</ns3:ImageLength>
<ns3:BitsPerSample>
<rdf:Seq>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
</rdf:Seq>
</ns3:BitsPerSample>
<ns3:PhotometricInterpretation>2</ns3:PhotometricInterpretation>
<ns3:SamplesPerPixel>3</ns3:SamplesPerPixel>
<ns3:WhitePoint>
<rdf:Seq>
<rdf:li>1343036288/4294967295</rdf:li>
<rdf:li>1413044224/4294967295</rdf:li>
</rdf:Seq>
</ns3:WhitePoint>
<ns3:PrimaryChromaticities>
<rdf:Seq>
<rdf:li>2748779008/4294967295</rdf:li>
<rdf:li>1417339264/4294967295</rdf:li>
<rdf:li>1288490240/4294967295</rdf:li>
<rdf:li>2576980480/4294967295</rdf:li>
<rdf:li>644245120/4294967295</rdf:li>
<rdf:li>257698032/4294967295</rdf:li>
</rdf:Seq>
</ns3:PrimaryChromaticities>
</rdf:Description>
<rdf:Description rdf:about="">
<ns4:ColorSpace>1</ns4:ColorSpace>
<ns4:PixelXDimension>2528</ns4:PixelXDimension>
<ns4:PixelYDimension>1424</ns4:PixelYDimension>
<ns4:FocalLength>353/100</ns4:FocalLength>
<ns4:GPSAltitudeRef>0</ns4:GPSAltitudeRef>
<ns4:GPSAltitude>0/1</ns4:GPSAltitude>
<ns4:GPSMapDatum>WGS-84</ns4:GPSMapDatum>
<ns4:DateTimeOriginal>2013-02-09T14:47:53</ns4:DateTimeOriginal>
<ns4:ISOSpeedRatings>
<rdf:Seq>
<rdf:li>76</rdf:li>
</rdf:Seq>
</ns4:ISOSpeedRatings>
<ns4:ExifVersion>0220</ns4:ExifVersion>
<ns4:FlashpixVersion>0100</ns4:FlashpixVersion>
<ns4:ComponentsConfiguration>
<rdf:Seq>
<rdf:li>1</rdf:li>
<rdf:li>2</rdf:li>
<rdf:li>3</rdf:li>
<rdf:li>0</rdf:li>
</rdf:Seq>
</ns4:ComponentsConfiguration>
<ns4:GPSLatitude>42,20.56N</ns4:GPSLatitude>
<ns4:GPSLongitude>71,5.29W</ns4:GPSLongitude>
<ns4:GPSTimeStamp>2013-02-09T19:47:53Z</ns4:GPSTimeStamp>
<ns4:GPSProcessingMethod>NETWORK</ns4:GPSProcessingMethod>
</rdf:Description>
<rdf:Description rdf:about="">
<ns5:DateCreated>2013-02-09T14:47:53</ns5:DateCreated>
</rdf:Description>
<rdf:Description rdf:about="">
<dc:Creator>
<rdf:Seq>
<rdf:li>Glymur</rdf:li>
<rdf:li>Python XMP Toolkit</rdf:li>
</rdf:Seq>
</dc:Creator>
</rdf:Description>
</rdf:RDF>
</ns0:xmpmeta>
<?xpacket end="w"?>
Contiguous Codestream Box (jp2c) @ (3223, 1132296)
SOC marker segment @ (3231, 0)
SIZ marker segment @ (3233, 47)
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)
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 @ (3282, 12)
Coding style:
Entropy coder, without partitions
SOP marker segments: False
EPH marker segments: False
Coding style parameters:
Progression order: LRCP
Number of layers: 2
Multiple component transformation usage: reversible
Number of resolutions: 2
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 @ (3296, 7)
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
CME marker segment @ (3305, 37)
"Created by OpenJPEG version 2.0.0"
SOT marker segment @ (3344, 10)
Tile part index: 0
Tile part length: 1132173
Tile part instance: 0
Number of tile parts: 1
COC marker segment @ (3356, 9)
Associated component: 1
Coding style for this component: Entropy coder, PARTITION = 0
Coding style parameters:
Number of resolutions: 2
Code block height, width: (64 x 64)
Wavelet transform: 5-3 reversible
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
QCC marker segment @ (3367, 8)
Associated Component: 1
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
COC marker segment @ (3377, 9)
Associated component: 2
Coding style for this component: Entropy coder, PARTITION = 0
Coding style parameters:
Number of resolutions: 2
Code block height, width: (64 x 64)
Wavelet transform: 5-3 reversible
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
QCC marker segment @ (3388, 8)
Associated Component: 2
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
SOD marker segment @ (3398, 0)
EOC marker segment @ (1135517, 0)"""
# Output of reader requirements printing for text_GBR.jp2 # Output of reader requirements printing for text_GBR.jp2
text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109) text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109)
Fully Understands Aspect Mask: 0xffff Fully Understands Aspect Mask: 0xffff

View file

@ -20,6 +20,13 @@ from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
class TestWarnings(unittest.TestCase): class TestWarnings(unittest.TestCase):
"""Test suite for warnings issued by glymur.""" """Test suite for warnings issued by glymur."""
def test_invalid_compatibility_list_entry(self):
"""should not error out with invalid compatibility list entry"""
filename = opj_data_file('input/nonregression/issue397.jp2')
with self.assertWarns(UserWarning):
Jp2k(filename)
self.assertTrue(True)
def test_exceeded_box_length(self): def test_exceeded_box_length(self):
""" """
should warn if reading past end of a box should warn if reading past end of a box
@ -62,7 +69,7 @@ class TestWarnings(unittest.TestCase):
\(\d+\)\.""", \(\d+\)\.""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile).get_codestream()
@unittest.skipIf(re.match("1.5|2.0.0", glymur.version.openjpeg_version), @unittest.skipIf(re.match("1.5|2.0.0", glymur.version.openjpeg_version),
"Test not passing on 1.5.x, not introduced until 2.x") "Test not passing on 1.5.x, not introduced until 2.x")
@ -77,7 +84,7 @@ class TestWarnings(unittest.TestCase):
\(\d+\)\.""", \(\d+\)\.""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile).get_codestream()
def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self): def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self):
""" """
@ -90,7 +97,7 @@ class TestWarnings(unittest.TestCase):
dx=\d+,\s*dy=\d+""", dx=\d+,\s*dy=\d+""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile).get_codestream()
def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self): def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self):
lst = ['input', 'nonregression', lst = ['input', 'nonregression',
@ -100,32 +107,32 @@ class TestWarnings(unittest.TestCase):
number\sof\scomponents\sis\sonly\s\d+""", number\sof\scomponents\sis\sonly\s\d+""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile).get_codestream()
def test_bad_rsiz(self): def test_bad_rsiz(self):
"""Should warn if RSIZ is bad. Issue196""" """Should warn if RSIZ is bad. Issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid profile'): with self.assertWarnsRegex(UserWarning, 'Invalid profile'):
Jp2k(filename) Jp2k(filename).get_codestream()
def test_bad_wavelet_transform(self): def test_bad_wavelet_transform(self):
"""Should warn if wavelet transform is bad. Issue195""" """Should warn if wavelet transform is bad. Issue195"""
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2') filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid wavelet transform'): with self.assertWarnsRegex(UserWarning, 'Invalid wavelet transform'):
Jp2k(filename) Jp2k(filename).get_codestream()
def test_invalid_progression_order(self): def test_invalid_progression_order(self):
"""Should still be able to parse even if prog order is invalid.""" """Should still be able to parse even if prog order is invalid."""
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2') jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid progression order'): with self.assertWarnsRegex(UserWarning, 'Invalid progression order'):
Jp2k(jfile) Jp2k(jfile).get_codestream()
def test_tile_height_is_zero(self): def test_tile_height_is_zero(self):
"""Zero tile height should not cause an exception.""" """Zero tile height should not cause an exception."""
filename = 'input/nonregression/2539.pdf.SIGFPE.706.1712.jp2' filename = 'input/nonregression/2539.pdf.SIGFPE.706.1712.jp2'
filename = opj_data_file(filename) filename = opj_data_file(filename)
with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'): with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'):
Jp2k(filename) Jp2k(filename).get_codestream()
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_unknown_marker_segment(self): def test_unknown_marker_segment(self):

View file

@ -1016,7 +1016,7 @@ class TestJp2Boxes(unittest.TestCase):
"""Raw instantiation should not produce a main_header.""" """Raw instantiation should not produce a main_header."""
box = ContiguousCodestreamBox() box = ContiguousCodestreamBox()
self.assertEqual(box.box_id, 'jp2c') self.assertEqual(box.box_id, 'jp2c')
self.assertIsNone(box.main_header) self.assertIsNone(box.codestream)
def test_codestream_main_header_offset(self): def test_codestream_main_header_offset(self):
"""main_header_offset is an attribute of the CCS box""" """main_header_offset is an attribute of the CCS box"""
@ -1318,7 +1318,7 @@ class TestRepr(MetadataBase):
# Difficult to eval(repr()) this, so just match the general pattern. # Difficult to eval(repr()) this, so just match the general pattern.
regexp = "glymur.jp2box.ContiguousCodeStreamBox" regexp = "glymur.jp2box.ContiguousCodeStreamBox"
regexp += "[(]main_header=<glymur.codestream.Codestream\sobject\s" regexp += "[(]codestream=<glymur.codestream.Codestream\sobject\s"
regexp += "at\s0x([a-fA-F0-9]*)>[)]" regexp += "at\s0x([a-fA-F0-9]*)>[)]"
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:

View file

@ -36,6 +36,9 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file
from . import fixtures from . import fixtures
def docTearDown(doctest_obj):
glymur.set_parseoptions(full_codestream=False)
# Doc tests should be run as well. # Doc tests should be run as well.
def load_tests(loader, tests, ignore): def load_tests(loader, tests, ignore):
"""Should run doc tests as well""" """Should run doc tests as well"""
@ -43,7 +46,8 @@ def load_tests(loader, tests, ignore):
# Can't do it on windows, temporary file issue. # Can't do it on windows, temporary file issue.
return tests return tests
if glymur.lib.openjp2.OPENJP2 is not None: if glymur.lib.openjp2.OPENJP2 is not None:
tests.addTests(doctest.DocTestSuite('glymur.jp2k')) tests.addTests(doctest.DocTestSuite('glymur.jp2k',
tearDown=docTearDown))
return tests return tests
@ -272,15 +276,6 @@ class TestJp2k(unittest.TestCase):
jp2 = Jp2k(jfile) jp2 = Jp2k(jfile)
self.assertEqual(jp2.shape, (128, 128)) self.assertEqual(jp2.shape, (128, 128))
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
def test_invalid_compatibility_list_entry(self):
"""should not error out with invalid compatibility list entry"""
filename = opj_data_file('input/nonregression/issue397.jp2')
with self.assertWarns(UserWarning):
Jp2k(filename)
self.assertTrue(True)
def test_shape_j2k(self): def test_shape_j2k(self):
"""verify shape attribute for J2K file """verify shape attribute for J2K file
""" """
@ -1052,31 +1047,30 @@ class TestParsing(unittest.TestCase):
def setUp(self): def setUp(self):
self.jp2file = glymur.data.nemo() self.jp2file = glymur.data.nemo()
# Reset parseoptions for every test. # Reset parseoptions for every test.
glymur.set_parseoptions(codestream=True) glymur.set_parseoptions(full_codestream=False)
def tearDown(self): def tearDown(self):
glymur.set_parseoptions(codestream=True) glymur.set_parseoptions(full_codestream=False)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_bad_rsiz(self): def test_bad_rsiz(self):
"""Should not warn if RSIZ when parsing is turned off.""" """Should not warn if RSIZ when parsing is turned off."""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
glymur.set_parseoptions(codestream=False) glymur.set_parseoptions(full_codestream=False)
Jp2k(filename) Jp2k(filename)
glymur.set_parseoptions(codestream=True) glymur.set_parseoptions(full_codestream=True)
with self.assertWarnsRegex(UserWarning, 'Invalid profile'): with self.assertWarnsRegex(UserWarning, 'Invalid profile'):
Jp2k(filename) Jp2k(filename)
def test_main_header(self): def test_main_header(self):
"""Verify that the main header isn't loaded when parsing turned off.""" """verify that the main header isn't loaded during normal parsing"""
# The hidden _main_header attribute should show up after accessing it. # The hidden _main_header attribute should show up after accessing it.
glymur.set_parseoptions(codestream=False)
jp2 = Jp2k(self.jp2file) jp2 = Jp2k(self.jp2file)
jp2c = jp2.box[4] jp2c = jp2.box[4]
self.assertIsNone(jp2c._main_header) self.assertIsNone(jp2c._codestream)
jp2c.main_header jp2c.codestream
self.assertIsNotNone(jp2c._main_header) self.assertIsNotNone(jp2c._codestream)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@ -1085,6 +1079,9 @@ class TestParsing(unittest.TestCase):
class TestJp2kOpjDataRootWarnings(unittest.TestCase): class TestJp2kOpjDataRootWarnings(unittest.TestCase):
"""These tests should be run by just about all configuration.""" """These tests should be run by just about all configuration."""
def tearDown(self):
glymur.set_parseoptions(full_codestream=False)
def test_undecodeable_box_id(self): def test_undecodeable_box_id(self):
"""Should warn in case of undecodeable box ID but not error out.""" """Should warn in case of undecodeable box ID but not error out."""
filename = opj_data_file('input/nonregression/edf_c2_1013627.jp2') filename = opj_data_file('input/nonregression/edf_c2_1013627.jp2')

View file

@ -385,7 +385,7 @@ class TestSuiteWarns(MetadataBase):
expected = ColourSpecificationBox(colorspace=glymur.core.SRGB) expected = ColourSpecificationBox(colorspace=glymur.core.SRGB)
self.verifyColourSpecificationBox(jp2.box[2].box[1], expected) self.verifyColourSpecificationBox(jp2.box[2].box[1], expected)
c = jp2.box[3].main_header c = jp2.box[3].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC'] expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC']

View file

@ -4,6 +4,7 @@ suite.
""" """
import re import re
import unittest import unittest
import warnings
import numpy as np import numpy as np
@ -52,7 +53,7 @@ class TestSuite(MetadataBase):
colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC) colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC)
self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr)
c = jp2.box[3].main_header c = jp2.box[3].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD'] expected = ['SOC', 'SIZ', 'COD', 'QCD']
@ -2584,7 +2585,7 @@ class TestSuite(MetadataBase):
self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1)) self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1))
self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2)) self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2))
c = jp2.box[4].main_header c = jp2.box[4].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD'] expected = ['SOC', 'SIZ', 'COD', 'QCD']
@ -2648,7 +2649,7 @@ class TestSuite(MetadataBase):
precedence=2) precedence=2)
self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr)
c = jp2.box[4].main_header c = jp2.box[4].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD'] expected = ['SOC', 'SIZ', 'COD', 'QCD']
@ -2711,7 +2712,7 @@ class TestSuite(MetadataBase):
self.assertEqual(jp2.box[2].box[2].channel_type, (0, 1)) # opacity self.assertEqual(jp2.box[2].box[2].channel_type, (0, 1)) # opacity
self.assertEqual(jp2.box[2].box[2].association, (0, 0)) # both main self.assertEqual(jp2.box[2].box[2].association, (0, 0)) # both main
c = jp2.box[3].main_header c = jp2.box[3].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD', 'CME'] expected = ['SOC', 'SIZ', 'COD', 'QCD', 'CME']
@ -2794,7 +2795,7 @@ class TestSuite(MetadataBase):
self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1)) self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1))
self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2)) self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2))
c = jp2.box[4].main_header c = jp2.box[4].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD'] expected = ['SOC', 'SIZ', 'COD', 'QCD']
@ -2848,7 +2849,7 @@ class TestSuite(MetadataBase):
colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC) colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC)
self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr)
c = jp2.box[3].main_header c = jp2.box[3].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD', 'POD'] expected = ['SOC', 'SIZ', 'COD', 'QCD', 'POD']
@ -2913,21 +2914,29 @@ class TestSuiteWarns(MetadataBase):
def test_NR_broken4_jp2_dump(self): def test_NR_broken4_jp2_dump(self):
jfile = opj_data_file('input/nonregression/broken4.jp2') jfile = opj_data_file('input/nonregression/broken4.jp2')
with self.assertWarns(UserWarning): with warnings.catch_warnings():
jp2 = Jp2k(jfile) # Suppress a warning, all we really care is parsing the entire
# file.
self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC') warnings.simplefilter("ignore")
with self.assertWarns(UserWarning):
jp2 = Jp2k(jfile)
self.assertEqual(jp2.box[-1].codestream.segment[-1].marker_id,
'QCC')
def test_NR_broken2_jp2_dump(self): def test_NR_broken2_jp2_dump(self):
""" """
Invalid marker ID in the codestream. Invalid marker ID in the codestream.
""" """
jfile = opj_data_file('input/nonregression/broken2.jp2') jfile = opj_data_file('input/nonregression/broken2.jp2')
with self.assertWarns(UserWarning): with warnings.catch_warnings():
# Invalid marker ID on codestream. # Suppress a warning, all we really care is parsing the entire
jp2 = Jp2k(jfile) # file.
warnings.simplefilter("ignore")
self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC') with self.assertWarns(UserWarning):
# Invalid marker ID on codestream.
jp2 = Jp2k(jfile)
self.assertEqual(jp2.box[-1].codestream.segment[-1].marker_id,
'QCC')
def test_NR_file1_dump(self): def test_NR_file1_dump(self):
jfile = opj_data_file('input/conformance/file1.jp2') jfile = opj_data_file('input/conformance/file1.jp2')
@ -3243,7 +3252,7 @@ class TestSuiteWarns(MetadataBase):
# Skip the 4th box, it is uknown. # Skip the 4th box, it is uknown.
c = jp2.box[4].main_header c = jp2.box[4].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD', 'CME', 'CME'] expected = ['SOC', 'SIZ', 'COD', 'QCD', 'CME', 'CME']
@ -3303,7 +3312,7 @@ class TestSuiteWarns(MetadataBase):
self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].icc_profile)
self.assertIsNone(jp2.box[2].box[1].colorspace) self.assertIsNone(jp2.box[2].box[1].colorspace)
c = jp2.box[3].main_header c = jp2.box[3].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD'] expected = ['SOC', 'SIZ', 'COD', 'QCD']
@ -3366,7 +3375,7 @@ class TestSuiteWarns(MetadataBase):
self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].icc_profile)
self.assertIsNone(jp2.box[2].box[1].colorspace) self.assertIsNone(jp2.box[2].box[1].colorspace)
c = jp2.box[3].main_header c = jp2.box[3].codestream
ids = [x.marker_id for x in c.segment] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'COD', 'QCD'] expected = ['SOC', 'SIZ', 'COD', 'QCD']

View file

@ -632,7 +632,7 @@ class TestSuiteWrite(fixtures.MetadataBase):
self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].icc_profile)
self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB)
codestream = jp2.box[3].main_header codestream = jp2.box[3].codestream
kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),

View file

@ -42,7 +42,7 @@ class TestPrinting(unittest.TestCase):
glymur.set_printoptions(short=False, xml=True, codestream=True) glymur.set_printoptions(short=False, xml=True, codestream=True)
def tearDown(self): def tearDown(self):
pass glymur.set_parseoptions(full_codestream=False)
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)"""
@ -854,7 +854,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
"""Should still be able to print if rsiz is bad, issue196""" """Should still be able to print if rsiz is bad, issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
j = Jp2k(filename) j = Jp2k(filename).get_codestream()
with patch('sys.stdout', new=StringIO()): with patch('sys.stdout', new=StringIO()):
print(j) print(j)
@ -1040,9 +1040,10 @@ class TestJp2dump(unittest.TestCase):
# Reset printoptions for every test. # Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True) glymur.set_printoptions(short=False, xml=True, codestream=True)
glymur.set_parseoptions(full_codestream=False)
def tearDown(self): def tearDown(self):
pass glymur.set_parseoptions(full_codestream=False)
def run_jp2dump(self, args): def run_jp2dump(self, args):
sys.argv = args sys.argv = args
@ -1055,25 +1056,51 @@ class TestJp2dump(unittest.TestCase):
return actual return actual
def test_default_nemo(self): def test_default_nemo(self):
"""Should be able to dump a JP2 file's metadata with no codestream.""" """by default one should get the main header"""
actual = self.run_jp2dump(['', self.jp2file]) actual = self.run_jp2dump(['', self.jp2file])
self.assertEqual(actual, fixtures.nemo_dump_no_codestream) # shave off the non-main-header segments
lines = fixtures.nemo.split('\n')
expected = lines[0:140]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
def test_codestream_0(self): @unittest.skipIf(sys.hexversion < 0x03000000, "assertRegex not in 2.7")
def test_jp2_codestream_0(self):
"""Verify dumping with -c 0, supressing all codestream details.""" """Verify dumping with -c 0, supressing all codestream details."""
actual = self.run_jp2dump(['', '-c', '0', self.jp2file]) actual = self.run_jp2dump(['', '-c', '0', self.jp2file])
expected = fixtures.nemo_dump_no_codestream expected = fixtures.nemo_dump_no_codestream
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_codestream_1(self): def test_jp2_codestream_1(self):
"""Verify dumping with -c 1, print just the header.""" """Verify dumping with -c 1, print just the header."""
actual = self.run_jp2dump(['', '-c', '1', self.jp2file]) actual = self.run_jp2dump(['', '-c', '1', self.jp2file])
self.assertEqual(actual, fixtures.nemo_with_codestream_header) # shave off the non-main-header segments
lines = fixtures.nemo.split('\n')
expected = lines[0:140]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
def test_codestream_2(self): def test_jp2_codestream_2(self):
"""Verify dumping with -c 2, print entire jp2 jacket, codestream."""
actual = self.run_jp2dump(['', '-c', '2', self.jp2file])
# shave off the non-main-header segments
expected = fixtures.nemo
self.assertEqual(actual, expected)
@unittest.skipIf(sys.hexversion < 0x03000000, "assertRegex not in 2.7")
def test_j2k_codestream_0(self):
"""-c 0 should print just a single line when used on a codestream."""
sys.argv = ['', '-c', '0', self.j2kfile]
with patch('sys.stdout', new=StringIO()) as fake_out:
command_line.main()
actual = fake_out.getvalue().strip()
self.assertRegex(actual, "File: .*")
def test_j2k_codestream_2(self):
"""Verify dumping with -c 2, full details.""" """Verify dumping with -c 2, full details."""
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
sys.argv = ['', '-c', '2', self.j2kfile] sys.argv = ['', '-c', '2', self.j2kfile]
@ -1098,13 +1125,9 @@ class TestJp2dump(unittest.TestCase):
"""Verify dumping with -x, suppress XML.""" """Verify dumping with -x, suppress XML."""
actual = self.run_jp2dump(['', '-x', self.jp2file]) actual = self.run_jp2dump(['', '-x', self.jp2file])
self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml) # shave off the XML and non-main-header segments
lines = fixtures.nemo.split('\n')
@unittest.skipIf(sys.hexversion < 0x03000000, "assertRegex not in 2.7") expected = lines[0:18]
def test_codestream_0_with_j2k_file(self): expected.extend(lines[104:140])
"""-c 0 should print just a single line when used on a codestream.""" expected = '\n'.join(expected)
sys.argv = ['', '-c', '0', self.j2kfile] self.assertEqual(actual, expected)
with patch('sys.stdout', new=StringIO()) as fake_out:
command_line.main()
actual = fake_out.getvalue().strip()
self.assertRegex(actual, "File: .*")