diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 161f8f6..6d9d7ce 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -595,7 +595,7 @@ class Jp2k(Jp2kBox): img = self._read_openjpeg(**kwargs) return img - def _read_openjpeg(self, reduce=0): + def _read_openjpeg(self, reduce=0, verbose=False): """Read a JPEG 2000 image using libopenjpeg. Parameters @@ -603,6 +603,8 @@ class Jp2k(Jp2kBox): reduce : int, optional Factor by which to reduce output resolution. Use -1 to get the lowest resolution thumbnail. + verbose : bool, optional + Print informational messages produced by the OpenJPEG library. Returns ------- @@ -629,6 +631,15 @@ class Jp2k(Jp2kBox): dinfo = opj._create_decompress(dparameters.decod_format) + event_mgr = opj.event_mgr_t() + info_handler = ctypes.cast(_info_callback, ctypes.c_void_p) + event_mgr.info_handler = info_handler if verbose else None + event_mgr.warning_handler = ctypes.cast(_warning_callback, + ctypes.c_void_p) + event_mgr.error_handler = ctypes.cast(_error_callback, + ctypes.c_void_p) + opj._set_event_mgr(dinfo, ctypes.byref(event_mgr)) + opj._setup_decoder(dinfo, dparameters) with open(self.filename, 'rb') as fp: diff --git a/glymur/lib/openjpeg.py b/glymur/lib/openjpeg.py index b288ab0..f818e1a 100644 --- a/glymur/lib/openjpeg.py +++ b/glymur/lib/openjpeg.py @@ -166,6 +166,16 @@ def _set_default_decoder_parameters(dparams_p): _OPENJPEG.opj_set_default_decoder_parameters(dparams_p) +def _set_event_mgr(dinfo, event_mgr, context=None): + """Wrapper for openjpeg library function opj_set_event_mgr. + """ + argtypes = [ctypes.POINTER(common_struct_t), + ctypes.POINTER(event_mgr_t), + ctypes.c_void_p] + _OPENJPEG.opj_set_event_mgr(ctypes.cast(dinfo, + ctypes.POINTER(common_struct_t)), + event_mgr, context) + def _setup_decoder(dinfo, dparams): """Wrapper for openjpeg library function opj_setup_decoder.""" argtypes = [ctypes.POINTER(dinfo_t), ctypes.POINTER(dparameters_t)] diff --git a/glymur/test/test_callbacks.py b/glymur/test/test_callbacks.py index 331e20e..2b5f7f6 100644 --- a/glymur/test/test_callbacks.py +++ b/glymur/test/test_callbacks.py @@ -59,5 +59,50 @@ class TestCallbacks(unittest.TestCase): self.assertEqual(actual, expected) +@unittest.skipIf(glymur.lib.openjp2._OPENJPEG is None, + "Missing openjpeg library.") +class TestCallbacks15(unittest.TestCase): + + @classmethod + def setUpClass(cls): + # Monkey patch the package so as to use OPENJPEG instead of OPENJP2 + cls.openjp2 = glymur.lib.openjp2._OPENJP2 + glymur.lib.openjp2._OPENJP2 = None + + @classmethod + def tearDownClass(cls): + # Restore OPENJP2 + glymur.lib.openjp2._OPENJP2 = cls.openjp2 + + def setUp(self): + # Save sys.stdout. + self.stdout = sys.stdout + sys.stdout = StringIO() + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() + + def tearDown(self): + # Restore stdout. + sys.stdout = self.stdout + + def test_info_callbacks_on_read(self): + # Verify that we get the expected stdio output when our internal info + # callback handler is enabled. + j = glymur.Jp2k(self.j2kfile) + d = j.read(reduce=1, verbose=True) + actual = sys.stdout.getvalue().strip() + + # We can't actually match this because the times will be different. + lines = ['[INFO] tile 1 of 1', + '[INFO] - tiers-1 took 0.050149 s', + '[INFO] - dwt took 0.004188 s', + '[INFO] - tile decoded in 0.058203 s'] + + expected = '\n'.join(lines) + self.assertTrue('[INFO]' in actual) + self.assertTrue('dwt took' in actual) + + + if __name__ == "__main__": unittest.main() diff --git a/glymur/test/test_jp2k_15.py b/glymur/test/test_jp2k_15.py index 6ac9802..5708cb4 100644 --- a/glymur/test/test_jp2k_15.py +++ b/glymur/test/test_jp2k_15.py @@ -35,6 +35,7 @@ class TestJp2k(unittest.TestCase): glymur.lib.openjp2._OPENJP2 = cls.openjp2 def setUp(self): + self.jp2file = glymur.data.nemo() self.j2kfile = glymur.data.goodstuff() def tearDown(self): @@ -64,10 +65,17 @@ class TestJp2k(unittest.TestCase): with self.assertRaises(TypeError) as ce: d = j2k.read(layer=1) - def test_basic(self): + def test_basic_jp2(self): # This test is only useful when openjp2 is not available # and OPJ_DATA_ROOT is not set. We need at least one - # working read test. + # working JP2 test. + j2k = Jp2k(self.jp2file) + d = j2k.read(reduce=1) + + def test_basic_j2k(self): + # This test is only useful when openjp2 is not available + # and OPJ_DATA_ROOT is not set. We need at least one + # working J2K test. j2k = Jp2k(self.j2kfile) d = j2k.read() @@ -309,7 +317,10 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_10_j2k(self): jfile = os.path.join(data_root, 'input/conformance/p0_10.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k.read(reduce=0) + with warnings.catch_warnings(): + # This file has an invalid ICC profile + warnings.simplefilter("ignore") + jpdata = jp2k.read(reduce=0) pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_10_0.pgx') pgxdata = read_pgx(pgxfile) @@ -695,7 +706,10 @@ class TestSuite(unittest.TestCase): def test_NR_DEC_kakadu_v4_4_openjpegv2_broken_j2k_16_decode(self): relpath = 'input/nonregression/kakadu_v4-4_openjpegv2_broken.j2k' jfile = os.path.join(data_root, relpath) - data = Jp2k(jfile).read() + with warnings.catch_warnings(): + # This file has an invalid ICC profile + warnings.simplefilter("ignore") + data = Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_MarkerIsNotCompliant_j2k_17_decode(self):