diff --git a/glymur/__init__.py b/glymur/__init__.py index 5826f8c..f39d6ef 100644 --- a/glymur/__init__.py +++ b/glymur/__init__.py @@ -9,6 +9,7 @@ __version__ = version.version from .jp2k import Jp2k from .jp2dump import jp2dump from .jp2box import get_printoptions, set_printoptions +from .jp2box import get_parseoptions, set_parseoptions from . import data diff --git a/glymur/jp2box.py b/glymur/jp2box.py index f541930..bf90f00 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1048,7 +1048,11 @@ class ContiguousCodestreamBox(Jp2kBox): ------- ContiguousCodestreamBox instance """ - box = cls(None, length=length, offset=offset) + if _parseoptions['codestream'] is True: + main_header = Codestream(fptr, length, header_only=True) + else: + main_header = None + box = cls(main_header, length=length, offset=offset) box._filename = fptr.name box._length = length box._offset = offset @@ -3233,6 +3237,50 @@ _BOX_WITH_ID = { b'uuid': UUIDBox, b'xml ': XMLBox} +_parseoptions = {'codestream': True} + +def set_parseoptions(codestream=True): + """Set parsing options. + + These options determine the way JPEG 2000 boxes are parsed. + + Parameters + ---------- + codestream : bool, defaults to True + When False, the codestream header is only parsed when accessed. This + can results in faster JP2/JPX parsing. + + See also + -------- + get_parseoptions + + Examples + -------- + To put back the default options, you can use: + + >>> import glymur + >>> glymur.set_parseoptions(codestream=True) + """ + _parseoptions['codestream'] = codestream + +def get_parseoptions(): + """Return the current parsing options. + + Returns + ------- + print_opts : dict + Dictionary of current print options with keys + + - codestream : bool + + For a full description of these options, see `set_parseoptions`. + + See also + -------- + set_parseoptions + """ + return _parseoptions + _printoptions = {'short': False, 'xml': True, 'codestream': True} def set_printoptions(**kwargs): diff --git a/glymur/test/test_icc.py b/glymur/test/test_icc.py index c49055e..d2dd959 100644 --- a/glymur/test/test_icc.py +++ b/glymur/test/test_icc.py @@ -35,7 +35,7 @@ class TestICC(unittest.TestCase): # 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 + profile = j.box[3].box[1].icc_profile self.assertEqual(profile['Size'], 546) self.assertEqual(profile['Preferred CMM Type'], 0) self.assertEqual(profile['Version'], '2.2.0') diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 6cd1ffd..a69f426 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -754,6 +754,35 @@ 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 TestParsing(unittest.TestCase): + """Tests for verifying how paring may be altered.""" + def setUp(self): + # Reset parseoptions for every test. + glymur.set_parseoptions(codestream=True) + + def tearDown(self): + pass + + def test_bad_rsiz(self): + """Should not warn if RSIZ when parsing is turned off.""" + # Actually there are three warning triggered by this codestream. + filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') + glymur.set_parseoptions(codestream=False) + with warnings.catch_warnings(record=True) as w: + j = Jp2k(filename) + self.assertEqual(len(w), 0) + + glymur.set_parseoptions(codestream=True) + with warnings.catch_warnings(record=True) as w: + j = Jp2k(filename) + self.assertEqual(len(w), 3) + + if sys.hexversion >= 0x03000000: + with self.assertWarns(UserWarning): + jp2 = Jp2k(filename) + @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") class TestJp2kOpjDataRoot(unittest.TestCase): diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 3c247fa..899a5b5 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -358,7 +358,11 @@ class TestSuite(unittest.TestCase): def test_ETS_JP2_file5(self): jfile = opj_data_file('input/conformance/file5.jp2') - jp2k = Jp2k(jfile) + with warnings.catch_warnings(): + # There's a warning for an unknown compatibility entry. + # Ignore it here. + warnings.simplefilter("ignore") + jp2k = Jp2k(jfile) jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768, 3)) @@ -3306,40 +3310,44 @@ class TestSuiteDump(unittest.TestCase): # profile and using the JPX-defined enumerated code for the ROMM-RGB # colourspace. jfile = opj_data_file('input/conformance/file5.jp2') - jp2 = Jp2k(jfile) + with warnings.catch_warnings(): + # There's a warning for an unknown compatibility entry. + # Ignore it here. + warnings.simplefilter("ignore") + jp2 = Jp2k(jfile) ids = [box.box_id for box in jp2.box] - self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) + self.assertEqual(ids, ['jP ', 'ftyp', 'rreq', 'jp2h', 'jp2c']) - ids = [box.box_id for box in jp2.box[2].box] - self.assertEqual(ids, ['ihdr', 'colr']) + ids = [box.box_id for box in jp2.box[3].box] + self.assertEqual(ids, ['ihdr', 'colr', '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, 'jp2 ') + self.assertEqual(jp2.box[1].brand, 'jpx ') self.assertEqual(jp2.box[1].minor_version, 0) self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ') # Jp2 Header # Image header - 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) + 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) # Jp2 Header # Colour specification - self.assertEqual(jp2.box[2].box[1].method, + self.assertEqual(jp2.box[3].box[1].method, glymur.core.RESTRICTED_ICC_PROFILE) # enumerated - 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) + 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) def test_NR_file6_dump(self): jfile = opj_data_file('input/conformance/file6.jp2') @@ -3390,37 +3398,37 @@ class TestSuiteDump(unittest.TestCase): jp2 = Jp2k(jfile) ids = [box.box_id for box in jp2.box] - self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) + self.assertEqual(ids, ['jP ', 'ftyp', 'rreq', 'jp2h', 'jp2c']) - ids = [box.box_id for box in jp2.box[2].box] - self.assertEqual(ids, ['ihdr', 'colr']) + ids = [box.box_id for box in jp2.box[3].box] + self.assertEqual(ids, ['ihdr', 'colr', '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, 'jp2 ') + self.assertEqual(jp2.box[1].brand, 'jpx ') self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ') # Jp2 Header # Image header - 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) + 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) # Jp2 Header # Colour specification - self.assertEqual(jp2.box[2].box[1].method, + self.assertEqual(jp2.box[3].box[1].method, glymur.core.RESTRICTED_ICC_PROFILE) - 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) + self.assertEqual(jp2.box[3].box[1].precedence, 0) + self.assertEqual(jp2.box[3].box[1].approximation, 1) + self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 13332) + self.assertIsNone(jp2.box[3].box[1].colorspace) def test_NR_file8_dump(self): # One 8-bit component in a gamma 1.8 space. The colourspace is @@ -3447,7 +3455,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, 3) + self.assertEqual(jp2.box[2].box[0].num_components, 1) 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