From c2a94b92f1d9fae7ac0b6a1190444aa0779a1660 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 09:54:04 -0400 Subject: [PATCH] First full run through successful with v2.0.0 official, with write support. #90 --- glymur/jp2k.py | 32 ++++++++++++++++++++++++++++++-- glymur/lib/openjp2.py | 11 +++++++++++ glymur/lib/test/test_openjp2.py | 7 ++++++- glymur/test/fixtures.py | 7 +++++++ glymur/test/test_jp2box.py | 4 ++++ glymur/test/test_jp2k.py | 21 +++++++++++++++++++++ glymur/test/test_opj_suite.py | 16 ++++++++++++++++ 7 files changed, 95 insertions(+), 3 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index e098ce9..beb0788 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -34,6 +34,14 @@ from .lib import _openjpeg as _opj from .lib import _openjp2 as _opj2 from .lib import c +# 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, 'gray': _opj2.CLRSPC_GRAY, 'grey': _opj2.CLRSPC_GRAY, @@ -371,6 +379,13 @@ class Jp2k(Jp2kBox): raise IOError(msg) numrows, numcols, num_comps = img_array.shape + if _OPENJP2_IS_OFFICIAL_V2: + if img_array.shape[2] != 1 and img_array.shape[2] != 3: + msg = "Writing images is restricted to single-channel " + msg += "greyscale images or three-channel RGB images when " + msg += "the OpenJPEG library version is the official 2.0.0 " + msg += "release." + raise IOError(msg) if colorspace is None: if img_array.shape[2] == 1 or img_array.shape[2] == 2: @@ -449,11 +464,24 @@ class Jp2k(Jp2kBox): _opj2.set_warning_handler(codec, _WARNING_CALLBACK) _opj2.set_error_handler(codec, _ERROR_CALLBACK) _opj2.setup_encoder(codec, cparams, image) - strm = _opj2.stream_create_default_file_stream_v3(self.filename, False) + + if hasattr(_opj2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): + strm = _opj2.stream_create_default_file_stream_v3(self.filename, + False) + else: + fptr = c.fopen(self.filename, 'wb') + strm = _opj2.stream_create_default_file_stream(fptr, False) + _opj2.start_compress(codec, image, strm) _opj2.encode(codec, strm) _opj2.end_compress(codec, strm) - _opj2.stream_destroy_v3(strm) + + if hasattr(_opj2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): + _opj2.stream_destroy_v3(strm) + else: + _opj2.stream_destroy(strm) + c.fclose(fptr) + _opj2.destroy_codec(codec) _opj2.image_destroy(image) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index 946b2e8..a92a231 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -5,6 +5,7 @@ Wraps individual functions in openjp2 library. # pylint: disable=C0302,R0903 import ctypes +import sys from .config import glymur_config OPENJP2, OPENJPEG = glymur_config() @@ -1382,3 +1383,13 @@ def set_error_message(msg): """The openjpeg error handler has recorded an error message.""" global ERROR_MSG_LST ERROR_MSG_LST.append(msg) + +def version(): + """Wrapper for opj_version library routine.""" + OPENJP2.opj_version.restype = ctypes.c_char_p + library_version = OPENJP2.opj_version() + if sys.hexversion >= 0x03000000: + return library_version.decode('utf-8') + else: + return library_version + diff --git a/glymur/lib/test/test_openjp2.py b/glymur/lib/test/test_openjp2.py index 771ff7a..00404bd 100644 --- a/glymur/lib/test/test_openjp2.py +++ b/glymur/lib/test/test_openjp2.py @@ -16,10 +16,15 @@ import numpy as np import glymur +OPENJP2_IS_V2_OFFICIAL=False +if glymur.lib.openjp2.OPENJP2 is not None: + if not hasattr(glymur.lib.openjp2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): + OPENJP2_IS_V2_OFFICIAL=True @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") -@unittest.skipIf(glymur.lib._openjp2.OPENJP2 is None, +@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, "Missing openjp2 library.") +@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, "API followed here specific to V2.0+") class TestOpenJP2(unittest.TestCase): def setUp(self): diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 3babc8a..54710ef 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -3,6 +3,13 @@ import sys import numpy as np +import glymur + +OPENJP2_IS_V2_OFFICIAL=False +if glymur.lib.openjp2.OPENJP2 is not None: + if not hasattr(glymur.lib.openjp2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): + OPENJP2_IS_V2_OFFICIAL=True + def mse(amat, bmat): """Mean Square Error""" diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index a2aa9d6..a563b58 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -19,6 +19,8 @@ from glymur.jp2box import * from glymur.core import COLOR, OPACITY from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE +from .fixtures import OPENJP2_IS_V2_OFFICIAL + try: format_corpus_data_root = os.environ['FORMAT_CORPUS_DATA_ROOT'] except KeyError: @@ -34,6 +36,8 @@ def load_tests(loader, tests, ignore): return tests +@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Requires v2.0.0+ in order to run.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, "Missing openjp2 library.") diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 34d49a5..06ab05f 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -26,6 +26,8 @@ import glymur from glymur import Jp2k from glymur.lib import openjp2 as opj2 +from .fixtures import OPENJP2_IS_V2_OFFICIAL + try: data_root = os.environ['OPJ_DATA_ROOT'] except KeyError: @@ -477,6 +479,8 @@ class TestJp2k(unittest.TestCase): self.assertEqual(j.box[2].box[1].colorspace, glymur.core.GREYSCALE) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Does not seem to work on official v2.0.0 release.") @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_grey_with_extra_component(self): with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: @@ -501,6 +505,8 @@ class TestJp2k(unittest.TestCase): self.assertEqual(j.box[2].box[1].colorspace, glymur.core.GREYSCALE) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Does not seem to work on official v2.0.0 release.") @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_rgb_with_extra_component(self): with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: @@ -512,6 +518,17 @@ class TestJp2k(unittest.TestCase): self.assertEqual(j.box[2].box[0].num_components, 4) self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL is False, + "Test is specific for v2.0.0 release") + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") + def test_extra_components_on_v2_official(self): + # Extra components seems to require 2.0+. Verify that we error out. + with self.assertRaises(IOError): + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') + data = np.zeros((128, 128, 4), dtype=np.uint8) + j.write(data) + def test_specify_ycc(self): # We don't support writing YCC at the moment. with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: @@ -655,6 +672,8 @@ class TestJp2k(unittest.TestCase): self.assertEqual(jasoc.box[3].box[1].box_id, 'xml ') @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Segfault on official v2.0.0 release.") def test_openjpeg_library_message(self): # Verify the error message produced by the openjpeg library. # This will confirm that the error callback mechanism is working. @@ -675,6 +694,8 @@ class TestJp2k(unittest.TestCase): tfile.write(data[3186:]) tfile.flush() + import shutil + shutil.copyfile(tfile.name, '/home/jevans/tmp/a.jp2') with warnings.catch_warnings(): warnings.simplefilter("ignore") j = Jp2k(tfile.name) diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 094680d..7091920 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -991,6 +991,8 @@ class TestSuite(unittest.TestCase): data = Jp2k(jfile).read() self.assertTrue(True) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test known to fail in v2.0.0 official") def test_NR_DEC_text_GBR_jp2_29_decode(self): jfile = os.path.join(data_root, 'input/nonregression/text_GBR.jp2') @@ -1007,12 +1009,16 @@ class TestSuite(unittest.TestCase): data = Jp2k(jfile).read() self.assertTrue(True) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test known to fail in v2.0.0 official") def test_NR_DEC_kodak_2layers_lrcp_j2c_31_decode(self): jfile = os.path.join(data_root, 'input/nonregression/kodak_2layers_lrcp.j2c') data = Jp2k(jfile).read() self.assertTrue(True) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test known to fail in v2.0.0 official") def test_NR_DEC_kodak_2layers_lrcp_j2c_32_decode(self): jfile = os.path.join(data_root, 'input/nonregression/kodak_2layers_lrcp.j2c') @@ -1025,6 +1031,8 @@ class TestSuite(unittest.TestCase): data = Jp2k(jfile).read() self.assertTrue(True) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test known to fail in v2.0.0 official") def test_NR_DEC_mem_b2ace68c_1381_jp2_34_decode(self): jfile = os.path.join(data_root, 'input/nonregression/mem-b2ace68c-1381.jp2') @@ -1035,6 +1043,8 @@ class TestSuite(unittest.TestCase): data = j.read() self.assertTrue(True) + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test known to fail in v2.0.0 official") def test_NR_DEC_mem_b2b86b74_2753_jp2_35_decode(self): jfile = os.path.join(data_root, 'input/nonregression/mem-b2b86b74-2753.jp2') @@ -1050,6 +1060,8 @@ class TestSuite(unittest.TestCase): with self.assertRaises(IOError): data = j.read() + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test not in done in v2.0.0 official") def test_NR_DEC_jp2_36_decode(self): lst = ('input', 'nonregression', @@ -1088,6 +1100,8 @@ class TestSuite(unittest.TestCase): with self.assertRaises(RuntimeError): data = Jp2k(jfile).read() + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test not in done in v2.0.0 official") @unittest.skipIf(sys.hexversion < 0x03020000, "Uses features introduced in 3.2.") def test_NR_DEC_issue188_beach_64bitsbox_jp2_41_decode(self): @@ -1098,6 +1112,8 @@ class TestSuite(unittest.TestCase): with self.assertWarns(UserWarning) as cw: data = Jp2k(jfile).read() + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, + "Test not in done in v2.0.0 official") def test_NR_DEC_issue206_image_000_jp2_42_decode(self): jfile = os.path.join(data_root, 'input/nonregression/issue206_image-000.jp2')