From 589dec51899df782d4725e6ba0cbeaba9ed086a6 Mon Sep 17 00:00:00 2001 From: jevans Date: Sat, 27 Jul 2013 21:23:40 -0400 Subject: [PATCH 01/24] Adding libc module for purpose of 2.0.0 support. --- glymur/lib/__init__.py | 1 + glymur/lib/c.py | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 glymur/lib/c.py diff --git a/glymur/lib/__init__.py b/glymur/lib/__init__.py index ba5915d..20fee5f 100644 --- a/glymur/lib/__init__.py +++ b/glymur/lib/__init__.py @@ -1,3 +1,4 @@ """This package organizes individual libraries employed by glymur.""" from . import openjp2 as _openjp2 from . import openjpeg as _openjpeg +from . import c diff --git a/glymur/lib/c.py b/glymur/lib/c.py new file mode 100644 index 0000000..7ae9fd6 --- /dev/null +++ b/glymur/lib/c.py @@ -0,0 +1,45 @@ +""" +Wraps fopen and fclose functions in libc. +""" + +import ctypes +import ctypes.util + +libc_path = ctypes.util.find_library('c') +C_LIB = ctypes.CDLL(libc_path) + +def fopen(filename, mode): + """Opens the file with the specified mode. + + Parameters + ---------- + filename : str + Path to filename. + mode : str + Specifies how the file is to be opened. + + Returns + ------- + fptr : ctypes.c_void_p + File pointer. + """ + C_LIB.fopen.restype = ctypes.c_void_p + C_LIB.fopen.argtypes = [ctypes.c_char_p, ctypes.c_char_p] + fptr = C_LIB.fopen(ctypes.c_char_p(filename.encode()), + ctypes.c_char_p(mode.encode())) + return fptr + +def fclose(fptr): + """Closes a file stream. + + Parameters + ---------- + fptr : ctypes.c_void_p + File pointer. + """ + C_LIB.fclose.argtypes = [ctypes.c_void_p] + C_LIB.fclose.restype = ctypes.c_int32 + status = C_LIB.fclose(fptr) + if status != 0: + raise IOError("Unable to close file.") + From b0566e47915f198b2d0c202b343091e669e7bf63 Mon Sep 17 00:00:00 2001 From: jevans Date: Sun, 28 Jul 2013 16:14:20 -0400 Subject: [PATCH 02/24] Some read support in 2.0.0. Some segfaults, though. #90 --- glymur/jp2k.py | 13 +++++++-- glymur/lib/openjp2.py | 67 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 2d9b220..15d4123 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -32,6 +32,7 @@ from .jp2box import ImageHeaderBox from .jp2box import ColourSpecificationBox from .lib import _openjpeg as _opj from .lib import _openjp2 as _opj2 +from .lib import c _COLORSPACE_MAP = {'rgb': _opj2.CLRSPC_SRGB, 'gray': _opj2.CLRSPC_GRAY, @@ -882,9 +883,15 @@ class Jp2k(Jp2kBox): dparam.nb_tile_to_decode = 1 with ExitStack() as stack: - stream = _opj2.stream_create_default_file_stream_v3(self.filename, - True) - stack.callback(_opj2.stream_destroy_v3, stream) + if hasattr(_opj2.OPENJPEG, 'opj_stream_create_default_file_stream_v3'): + stream = _opj2.stream_create_default_file_stream_v3(self.filename, + True) + stack.callback(_opj2.stream_destroy_v3, stream) + else: + fptr = c.fopen(self.filename, 'rb') + stack.callback(c.fclose, fptr) + stream = _opj2.stream_create_default_file_stream(fptr, True) + stack.callback(_opj2.stream_destroy, stream) codec = _opj2.create_decompress(self._codec_format) stack.callback(_opj2.destroy_codec, codec) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index be9981b..946b2e8 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -639,9 +639,16 @@ if OPENJP2 is not None: ctypes.POINTER(ImageType)] OPENJP2.opj_setup_encoder.argtypes = ARGTYPES - ARGTYPES = [ctypes.c_char_p, ctypes.c_int32] - OPENJP2.opj_stream_create_default_file_stream_v3.argtypes = ARGTYPES - OPENJP2.opj_stream_create_default_file_stream_v3.restype = STREAM_TYPE_P + if hasattr(OPENJP2, 'opj_stream_create_default_file_stream_v3'): + ARGTYPES = [ctypes.c_char_p, ctypes.c_int32] + OPENJP2.opj_stream_create_default_file_stream_v3.argtypes = ARGTYPES + OPENJP2.opj_stream_create_default_file_stream_v3.restype = STREAM_TYPE_P + OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P] + else: + ARGTYPES = [ctypes.c_void_p, ctypes.c_int32] + OPENJP2.opj_stream_create_default_file_stream.argtypes = ARGTYPES + OPENJP2.opj_stream_create_default_file_stream.restype = STREAM_TYPE_P + OPENJP2.opj_stream_destroy.argtypes = [STREAM_TYPE_P] ARGTYPES = [CODEC_TYPE, ctypes.POINTER(ImageType), STREAM_TYPE_P] OPENJP2.opj_start_compress.argtypes = ARGTYPES @@ -649,7 +656,6 @@ if OPENJP2 is not None: OPENJP2.opj_end_compress.argtypes = [CODEC_TYPE, STREAM_TYPE_P] OPENJP2.opj_end_decompress.argtypes = [CODEC_TYPE, STREAM_TYPE_P] - OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P] OPENJP2.opj_destroy_codec.argtypes = [CODEC_TYPE] ARGTYPES = [CODEC_TYPE, @@ -1267,16 +1273,17 @@ def start_compress(codec, image, stream): OPENJP2.opj_start_compress(codec, image, stream) -def stream_create_default_file_stream_v3(fname, a_read_stream): - """Wraps openjp2 library function opj_stream_create_default_vile_stream_v3. +def stream_create_default_file_stream(fptr, isa_read_stream): + """Wraps openjp2 library function opj_stream_create_default_vile_stream. - Sets the stream to be a file stream. + Sets the stream to be a file stream. This is valid only for version 2.0.0 + of OpenJPEG. Parameters ---------- - fname : str - Specifies a file. - a_read_stream: bool + fptr : ctypes.c_void_p + Corresponds to C file pointer. Must be obtained from libc.fopen. + isa_read_stream: bool True (read) or False (write) Returns @@ -1284,16 +1291,52 @@ def stream_create_default_file_stream_v3(fname, a_read_stream): stream : stream_t An OpenJPEG file stream. """ - read_stream = 1 if a_read_stream else 0 + read_stream = 1 if isa_read_stream else 0 + stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream) + return stream + + +def stream_create_default_file_stream_v3(fname, isa_read_stream): + """Wraps openjp2 library function opj_stream_create_default_vile_stream_v3. + + Sets the stream to be a file stream. This function is only valid for the + trunk/development 2.0+ version of the openjp2 library. + + Parameters + ---------- + fname : str + Specifies a file. + isa_read_stream: bool + True (read) or False (write) + + Returns + ------- + stream : stream_t + An OpenJPEG file stream. + """ + read_stream = 1 if isa_read_stream else 0 file_argument = ctypes.c_char_p(fname.encode()) stream = OPENJP2.opj_stream_create_default_file_stream_v3(file_argument, read_stream) return stream -def stream_destroy_v3(stream): +def stream_destroy(stream): """Wraps openjp2 library function opj_stream_destroy. + Destroys the stream created by create_stream. + + Parameters + ---------- + stream : STREAM_TYPE_P + The file stream. + """ + OPENJP2.opj_stream_destroy(stream) + + +def stream_destroy_v3(stream): + """Wraps openjp2 library function opj_stream_destroy_v3. + Destroys the stream created by create_stream_v3. Parameters From 48b95a235da24182bb3db8319a7373cff6fcf41e Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 06:58:55 -0400 Subject: [PATCH 03/24] Starting to filter out tests handled by 2.0+ but not 2.0.0 official. #90 --- glymur/jp2k.py | 2 +- glymur/test/test_opj_suite.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 15d4123..e098ce9 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -883,7 +883,7 @@ class Jp2k(Jp2kBox): dparam.nb_tile_to_decode = 1 with ExitStack() as stack: - if hasattr(_opj2.OPENJPEG, 'opj_stream_create_default_file_stream_v3'): + if hasattr(_opj2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): stream = _opj2.stream_create_default_file_stream_v3(self.filename, True) stack.callback(_opj2.stream_destroy_v3, stream) diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 47d50f8..094680d 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -30,6 +30,11 @@ import numpy as np from glymur import Jp2k 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 + from .fixtures import * try: @@ -1056,6 +1061,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_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self): relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2' jfile = os.path.join(data_root, relpath) @@ -1065,6 +1072,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_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self): relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2' jfile = os.path.join(data_root, relpath) From c2a94b92f1d9fae7ac0b6a1190444aa0779a1660 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 09:54:04 -0400 Subject: [PATCH 04/24] 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') From 429d0f8f1fc4a26fe13d4a726c7a49b2ae9102be Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 11:26:32 -0400 Subject: [PATCH 05/24] Abstracted out all the write parameter validation. #90 RuntimeErrors swapped out for IOError. --- glymur/jp2k.py | 201 +++++++++++++++++++----------- glymur/test/test_opj_suite_neg.py | 8 +- 2 files changed, 132 insertions(+), 77 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index beb0788..1d751e0 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -32,7 +32,7 @@ from .jp2box import ImageHeaderBox from .jp2box import ColourSpecificationBox from .lib import _openjpeg as _opj from .lib import _openjp2 as _opj2 -from .lib import c +from .lib import c as _libc # Need to known if openjp2 library is the officially release v2.0.0 or not. _OPENJP2_IS_OFFICIAL_V2 = False @@ -187,6 +187,94 @@ class Jp2k(Jp2kBox): msg += "profile if the file type box brand is 'jp2 '." warnings.warn(msg) + def _validate_write_parameters(self, img_array, code_block_size, precinct_sizes, cratios, + psnr, mct, colorspace, codec_fmt): + """Check that the input parameters to the write function are valid. + + Parameters + ---------- + img_array : ndarray + Image data to be written to file. + code_block_size : tuple + Code block size (DY, DX). + precinct_sizes : list + List of precinct sizes. Each precinct size tuple is defined in + (height x width). + cratios : iterable + Compression ratios for successive layers. + psnr : iterable + Different PSNR for successive layers. + mct : bool + Specifies usage of the multi component transform. If not + specified, defaults to True if the colorspace is RGB. + colorspace : str, optional + Either 'rgb' or 'gray'. + codec_fmt : int + Are we writing a JP2 file or a J2K file? + """ + # Validate code block size and precinct sizes. + if code_block_size is not None: + width = code_block_size[1] + height = code_block_size[0] + if height * width > 4096 or height < 4 or width < 4: + msg = "Code block area cannot exceed 4096. " + msg += "Code block height and width must be larger than 4." + raise IOError(msg) + if ((math.log(height, 2) != math.floor(math.log(height, 2)) or + math.log(width, 2) != math.floor(math.log(width, 2)))): + msg = "Bad code block size ({0}, {1}), " + msg += "must be powers of 2." + raise IOError(msg.format(height, width)) + + if precinct_sizes is not None: + for j, (prch, prcw) in enumerate(precinct_sizes): + if j == 0 and code_block_size is not None: + cblkh, cblkw = code_block_size + if cblkh * 2 > prch or cblkw * 2 > prcw: + msg = "Highest Resolution precinct size must be at " + msg += "least twice that of the code block dimensions." + raise IOError(msg) + if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or + math.log(prcw, 2) != math.floor(math.log(prcw, 2)))): + msg = "Bad precinct sizes ({0}, {1}), " + msg += "must be powers of 2." + raise IOError(msg.format(prch, prcw)) + + if cratios is not None and psnr is not None: + msg = "Cannot specify cratios and psnr together." + raise IOError(msg) + + # What would the point of 1D images be? + if img_array.ndim == 1 or img_array.ndim > 3: + msg = "{0}D imagery is not allowed.".format(img_array.ndim) + raise IOError(msg) + + if _OPENJP2_IS_OFFICIAL_V2: + if (((img_array.ndim != 2) and + (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 not None: + if codec_fmt == _opj2.CODEC_J2K: + msg = 'Do not specify a colorspace when writing a raw ' + msg += 'codestream.' + raise IOError(msg) + if colorspace.lower() not in ('rgb', 'grey', 'gray'): + msg = 'Invalid colorspace "{0}"'.format(colorspace) + raise IOError(msg) + elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3: + msg = 'RGB colorspace requires at least 3 components.' + raise IOError(msg) + + if img_array.dtype != np.uint8 and img_array.dtype != np.uint16: + msg = "Only uint8 and uint16 images are currently supported." + raise RuntimeError(msg) + + # pylint: disable-msg=W0221 def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None, cbsize=None, psizes=None, grid_offset=None, sop=False, @@ -211,7 +299,7 @@ class Jp2k(Jp2kBox): Code block size (DY, DX). colorspace : str, optional Either 'rgb' or 'gray'. - cratios : sequence, optional + cratios : iterable Compression ratios for successive layers. eph : bool, optional If true, write SOP marker after each header packet. @@ -232,7 +320,7 @@ class Jp2k(Jp2kBox): Number of resolutions. prog : str, optional Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL". - psnr : list, optional + psnr : iterable, optional Different PSNR for successive layers. psizes : list, optional List of precinct sizes. Each precinct size tuple is defined in @@ -264,9 +352,17 @@ class Jp2k(Jp2kBox): If glymur is unable to load the openjp2 library. """ if _opj2.OPENJP2 is None: - raise LibraryNotFoundError("You must have the development version " - "of OpenJP2 installed before using " - "this functionality.") + raise LibraryNotFoundError("You must have the openjp2 library " + "installed before using this " + "functionality.") + + if self.filename[-4:].lower() == '.jp2': + codec_fmt = _opj2.CODEC_JP2 + else: + codec_fmt = _opj2.CODEC_J2K + + self._validate_write_parameters(img_array, cbsize, psizes, cratios, + psnr, mct, colorspace, codec_fmt) cparams = _opj2.set_default_encoder_parameters() @@ -275,11 +371,6 @@ class Jp2k(Jp2kBox): outfile += b'0' * num_pad_bytes cparams.outfile = outfile - if self.filename[-4:].lower() == '.jp2': - codec_fmt = _opj2.CODEC_JP2 - else: - codec_fmt = _opj2.CODEC_J2K - cparams.cod_format = codec_fmt # Set defaults to lossless to begin. @@ -290,15 +381,6 @@ class Jp2k(Jp2kBox): if cbsize is not None: width = cbsize[1] height = cbsize[0] - if height * width > 4096 or height < 4 or width < 4: - msg = "Code block area cannot exceed 4096. " - msg += "Code block height and width must be larger than 4." - raise RuntimeError(msg) - if ((math.log(height, 2) != math.floor(math.log(height, 2)) or - math.log(width, 2) != math.floor(math.log(width, 2)))): - msg = "Bad code block size ({0}, {1}), " - msg += "must be powers of 2." - raise IOError(msg.format(height, width)) cparams.cblockw_init = width cparams.cblockh_init = height @@ -336,18 +418,6 @@ class Jp2k(Jp2kBox): if psizes is not None: for j, (prch, prcw) in enumerate(psizes): - if j == 0 and cbsize is not None: - cblkh, cblkw = cbsize - if cblkh * 2 > prch or cblkw * 2 > prcw: - msg = "Highest Resolution precinct size must be at " - msg += "least twice that of the code block dimensions." - raise IOError(msg) - if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or - math.log(prcw, 2) != math.floor(math.log(prcw, 2)))): - msg = "Bad precinct sizes ({0}, {1}), " - msg += "must be powers of 2." - raise IOError(msg.format(prch, prcw)) - cparams.prcw_init[j] = prcw cparams.prch_init[j] = prch cparams.csty |= 0x01 @@ -365,54 +435,38 @@ class Jp2k(Jp2kBox): cparams.cp_tdy = tilesize[0] cparams.tile_size_on = _opj2.TRUE - if cratios is not None and psnr is not None: - msg = "Cannot specify cratios and psnr together." - raise RuntimeError(msg) - if img_array.ndim == 2: + # Force it to be 3D. Just makes things easier later on. numrows, numcols = img_array.shape img_array = img_array.reshape(numrows, numcols, 1) - elif img_array.ndim == 3: - pass - else: - msg = "{0}D imagery is not allowed.".format(img_array.ndim) - 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: + # Must infer the colorspace from the image dimensions. if img_array.shape[2] == 1 or img_array.shape[2] == 2: + # A single channel image or an image with two channels is going + # to be greyscale. colorspace = _opj2.CLRSPC_GRAY else: - # No YCC unless specifically told to do so. + # Anything else must be RGB, right? colorspace = _opj2.CLRSPC_SRGB else: - if codec_fmt == _opj2.CODEC_J2K: - raise IOError('Do not specify a colorspace with J2K.') - colorspace = colorspace.lower() - if colorspace not in ('rgb', 'grey', 'gray'): - msg = 'Invalid colorspace "{0}"'.format(colorspace) - raise IOError(msg) - elif colorspace == 'rgb' and img_array.shape[2] < 3: - msg = 'RGB colorspace requires at least 3 components.' - raise IOError(msg) - else: - colorspace = _COLORSPACE_MAP[colorspace] + # Turn the colorspace from a string to the enumerated value that + # the library expects. + colorspace = _COLORSPACE_MAP[colorspace.lower()] if mct is None: + # If the multi component transform was not specified, we infer + # that it should be used if the color space is RGB. if colorspace == _opj2.CLRSPC_SRGB: cparams.tcp_mct = 1 else: cparams.tcp_mct = 0 else: if mct and colorspace == _opj2.CLRSPC_GRAY: + # Cannot check for this in the validate routine, as we need + # to know what the target colorspace has been determined to be. msg = "Cannot specify usage of the multi component transform " msg += "if the colorspace is gray." raise IOError(msg) @@ -420,10 +474,9 @@ class Jp2k(Jp2kBox): if img_array.dtype == np.uint8: comp_prec = 8 - elif img_array.dtype == np.uint16: - comp_prec = 16 else: - raise RuntimeError("unhandled datatype") + # We already know it cannot be anything else than uint16. + comp_prec = 16 comptparms = (_opj2.ImageComptParmType * num_comps)() for j in range(num_comps): @@ -465,26 +518,28 @@ class Jp2k(Jp2kBox): _opj2.set_error_handler(codec, _ERROR_CALLBACK) _opj2.setup_encoder(codec, cparams, image) - if hasattr(_opj2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): + if _OPENJP2_IS_OFFICIAL_V2: + fptr = _libc.fopen(self.filename, 'wb') + strm = _opj2.stream_create_default_file_stream(fptr, False) + else: 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) + # Start to clean up after ourselves. _opj2.start_compress(codec, image, strm) _opj2.encode(codec, strm) _opj2.end_compress(codec, strm) - if hasattr(_opj2.OPENJP2, 'opj_stream_create_default_file_stream_v3'): - _opj2.stream_destroy_v3(strm) - else: + if _OPENJP2_IS_OFFICIAL_V2: _opj2.stream_destroy(strm) - c.fclose(fptr) + _libc.fclose(fptr) + else: + _opj2.stream_destroy_v3(strm) _opj2.destroy_codec(codec) _opj2.image_destroy(image) + # Refresh the metadata. self.parse() def wrap(self, filename, boxes=None): @@ -916,8 +971,8 @@ class Jp2k(Jp2kBox): True) stack.callback(_opj2.stream_destroy_v3, stream) else: - fptr = c.fopen(self.filename, 'rb') - stack.callback(c.fclose, fptr) + fptr = _libc.fopen(self.filename, 'rb') + stack.callback(_libc.fclose, fptr) stream = _opj2.stream_create_default_file_stream(fptr, True) stack.callback(_opj2.stream_destroy, stream) codec = _opj2.create_decompress(self._codec_format) diff --git a/glymur/test/test_opj_suite_neg.py b/glymur/test/test_opj_suite_neg.py index 48b8117..3dfda15 100644 --- a/glymur/test/test_opj_suite_neg.py +++ b/glymur/test/test_opj_suite_neg.py @@ -70,7 +70,7 @@ class TestSuiteNegative(unittest.TestCase): data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: j = Jp2k(tfile.name, 'wb') - with self.assertRaises(RuntimeError): + with self.assertRaises(IOError): j.write(data, psnr=[30, 35, 40], cratios=[2, 3, 4]) def test_NR_MarkerIsNotCompliant_j2k_dump(self): @@ -109,13 +109,13 @@ class TestSuiteNegative(unittest.TestCase): j = Jp2k(tfile.name, 'wb') # opj_compress doesn't allow code block area to exceed 4096. - with self.assertRaises(RuntimeError) as cr: + with self.assertRaises(IOError) as cr: j.write(data, cbsize=(256, 256)) # opj_compress doesn't allow either dimension to be less than 4. - with self.assertRaises(RuntimeError) as cr: + with self.assertRaises(IOError) as cr: j.write(data, cbsize=(2048, 2)) - with self.assertRaises(RuntimeError) as cr: + with self.assertRaises(IOError) as cr: j.write(data, cbsize=(2, 2048)) @unittest.skipIf(sys.hexversion < 0x03020000, From 8120c212995e2a4667c7167e4715aea0ddc3d0c6 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 11:40:47 -0400 Subject: [PATCH 06/24] Pep8 work. #90 --- glymur/jp2k.py | 8 ++++---- glymur/lib/c.py | 3 ++- glymur/lib/config.py | 2 +- glymur/lib/openjp2.py | 2 +- glymur/lib/test/test_openjp2.py | 8 +++++--- glymur/test/fixtures.py | 7 ++++--- glymur/test/test_opj_suite.py | 5 +---- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 1d751e0..897d6a6 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -187,8 +187,9 @@ class Jp2k(Jp2kBox): msg += "profile if the file type box brand is 'jp2 '." warnings.warn(msg) - def _validate_write_parameters(self, img_array, code_block_size, precinct_sizes, cratios, - psnr, mct, colorspace, codec_fmt): + def _validate_write_parameters(self, img_array, code_block_size, + precinct_sizes, cratios, psnr, mct, + colorspace, codec_fmt): """Check that the input parameters to the write function are valid. Parameters @@ -250,7 +251,7 @@ class Jp2k(Jp2kBox): raise IOError(msg) if _OPENJP2_IS_OFFICIAL_V2: - if (((img_array.ndim != 2) and + if (((img_array.ndim != 2) and (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 " @@ -274,7 +275,6 @@ class Jp2k(Jp2kBox): msg = "Only uint8 and uint16 images are currently supported." raise RuntimeError(msg) - # pylint: disable-msg=W0221 def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None, cbsize=None, psizes=None, grid_offset=None, sop=False, diff --git a/glymur/lib/c.py b/glymur/lib/c.py index 7ae9fd6..984adc5 100644 --- a/glymur/lib/c.py +++ b/glymur/lib/c.py @@ -8,6 +8,7 @@ import ctypes.util libc_path = ctypes.util.find_library('c') C_LIB = ctypes.CDLL(libc_path) + def fopen(filename, mode): """Opens the file with the specified mode. @@ -29,6 +30,7 @@ def fopen(filename, mode): ctypes.c_char_p(mode.encode())) return fptr + def fclose(fptr): """Closes a file stream. @@ -42,4 +44,3 @@ def fclose(fptr): status = C_LIB.fclose(fptr) if status != 0: raise IOError("Unable to close file.") - diff --git a/glymur/lib/config.py b/glymur/lib/config.py index 764934e..bbdb056 100644 --- a/glymur/lib/config.py +++ b/glymur/lib/config.py @@ -145,7 +145,7 @@ def get_configdir(): if 'HOME' in os.environ and os.name != 'nt': # HOME is set by WinPython to something unusual, so we don't - # necessarily want that. + # necessarily want that. return os.path.join(os.environ['HOME'], '.config', 'glymur') # Last stand. Should handle windows... others? diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index a92a231..66a3cba 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -1384,6 +1384,7 @@ def set_error_message(msg): 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 @@ -1392,4 +1393,3 @@ def version(): 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 00404bd..710f265 100644 --- a/glymur/lib/test/test_openjp2.py +++ b/glymur/lib/test/test_openjp2.py @@ -16,10 +16,12 @@ import numpy as np import glymur -OPENJP2_IS_V2_OFFICIAL=False +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 + 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, diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 54710ef..3e5cfbb 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -5,10 +5,11 @@ import numpy as np import glymur -OPENJP2_IS_V2_OFFICIAL=False +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 + if not hasattr(glymur.lib.openjp2.OPENJP2, + 'opj_stream_create_default_file_stream_v3'): + OPENJP2_IS_V2_OFFICIAL = True def mse(amat, bmat): diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 7091920..b42e033 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -30,10 +30,7 @@ import numpy as np from glymur import Jp2k 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 +from .fixtures import OPENJP2_IS_V2_OFFICIAL from .fixtures import * From 372bfc2943f2e991868fa998a928f2fd72dfbac0 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 11:55:39 -0400 Subject: [PATCH 07/24] Pylint work. #90 --- glymur/jp2box.py | 73 ------------------------------------------------ glymur/jp2k.py | 6 ++-- glymur/lib/c.py | 4 +-- 3 files changed, 5 insertions(+), 78 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 4949752..c264b7b 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -636,79 +636,6 @@ class CompositingLayerHeaderBox(Jp2kBox): return box -class JP2HeaderBox(Jp2kBox): - """Container for JP2 header box information. - - Attributes - ---------- - box_id : str - 4-character identifier for the box. - length : int - length of the box in bytes. - offset : int - offset of the box from the start of the file. - longname : str - more verbose description of the box. - box : list - List of boxes contained in this superbox. - """ - def __init__(self, length=0, offset=-1): - Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header') - self.length = length - self.offset = offset - self.box = [] - - def __str__(self): - msg = Jp2kBox.__str__(self) - for box in self.box: - boxstr = str(box) - - # Add indentation. - strs = [('\n ' + x) for x in boxstr.split('\n')] - msg += ''.join(strs) - return msg - - def write(self, fptr): - """Write a JP2 Header box to file. - """ - # Write the contained boxes, then come back and write the length. - orig_pos = fptr.tell() - fptr.write(struct.pack('>I', 0)) - fptr.write('jp2h'.encode()) - for box in self.box: - box.write(fptr) - - end_pos = fptr.tell() - fptr.seek(orig_pos) - fptr.write(struct.pack('>I', end_pos - orig_pos)) - fptr.seek(end_pos) - - @staticmethod - def parse(fptr, offset, length): - """Parse JPEG 2000 header box. - - Parameters - ---------- - fptr : file - Open file object. - offset : int - Start position of box in bytes. - length : int - Length of the box in bytes. - - Returns - ------- - JP2HeaderBox instance - """ - box = JP2HeaderBox(length=length, offset=offset) - - # The JP2 header box is a superbox, so go ahead and parse its child - # boxes. - box.box = box.parse_superbox(fptr) - - return box - - class ComponentMappingBox(Jp2kBox): """Container for channel identification information. diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 897d6a6..8dba499 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -188,8 +188,8 @@ class Jp2k(Jp2kBox): warnings.warn(msg) def _validate_write_parameters(self, img_array, code_block_size, - precinct_sizes, cratios, psnr, mct, - colorspace, codec_fmt): + precinct_sizes, cratios, psnr, colorspace, + codec_fmt): """Check that the input parameters to the write function are valid. Parameters @@ -362,7 +362,7 @@ class Jp2k(Jp2kBox): codec_fmt = _opj2.CODEC_J2K self._validate_write_parameters(img_array, cbsize, psizes, cratios, - psnr, mct, colorspace, codec_fmt) + psnr, colorspace, codec_fmt) cparams = _opj2.set_default_encoder_parameters() diff --git a/glymur/lib/c.py b/glymur/lib/c.py index 984adc5..4a51551 100644 --- a/glymur/lib/c.py +++ b/glymur/lib/c.py @@ -5,8 +5,8 @@ Wraps fopen and fclose functions in libc. import ctypes import ctypes.util -libc_path = ctypes.util.find_library('c') -C_LIB = ctypes.CDLL(libc_path) +LIBC_PATH = ctypes.util.find_library('c') +C_LIB = ctypes.CDLL(LIBC_PATH) def fopen(filename, mode): From ca740f90f9e4047b5cd0adaffa4ce6c534c54e7f Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 29 Jul 2013 17:49:43 -0400 Subject: [PATCH 08/24] Need to infer the location of openjp2 when not found by ctypes. #90 --- glymur/lib/config.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/glymur/lib/config.py b/glymur/lib/config.py index bbdb056..72d91a9 100644 --- a/glymur/lib/config.py +++ b/glymur/lib/config.py @@ -104,6 +104,12 @@ def load_openjp2(libopenjp2_path): # No help from the config file, try to find it ourselves. libopenjp2_path = find_library('openjp2') + if libopenjp2_path is None: + if platform.system() == 'Darwin': + path = '/opt/local/lib/libopenjp2.dylib' + if os.path.exists(path): + libopenjp2_path = path + if libopenjp2_path is None: return None From 39ae3d423b15e885c4d7d60a27d0d48829d8e6ea Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 29 Jul 2013 19:48:21 -0400 Subject: [PATCH 09/24] Updated docs for 2.0 support. #90 --- docs/source/detailed_installation.rst | 23 +++++++++++------------ docs/source/introduction.rst | 17 ++++++++--------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst index 165868f..84fe887 100644 --- a/docs/source/detailed_installation.rst +++ b/docs/source/detailed_installation.rst @@ -9,13 +9,14 @@ Glymur Configuration The default glymur installation process relies upon OpenJPEG version 1.X being properly installed on your system. This will, however, only give you you basic read capabilities, so if you wish to take advantage -of more of glymur's features, you should compile OpenJPEG as a shared -library (named *openjp2* instead of *openjpeg*) from the developmental -source that you can retrieve via subversion. As of this time of writing, -svn revision 2345 works. You should also download the test data for -the purpose of configuring and running OpenJPEG's test suite, check -their instructions for all this. You should set the **OPJ_DATA_ROOT** -environment variable for the purpose of running Glymur's test suite. :: +of more of glymur's features, you should install version 2.0 or +compile OpenJPEG as a shared library (named *openjp2* instead of +*openjpeg*) from the developmental source that you can retrieve via +subversion. As of this time of writing, svn revision 2345 works. +You should also download the test data for the purpose of configuring +and running OpenJPEG's test suite, check their instructions for all +this. You should set the **OPJ_DATA_ROOT** environment variable +for the purpose of running Glymur's test suite. :: $ svn co http://openjpeg.googlecode.com/svn/data $ export OPJ_DATA_ROOT=`pwd`/data @@ -40,8 +41,8 @@ but if you have **$XDG_CONFIG_HOME** defined, the path will be :: $XDG_CONFIG_HOME/glymur/glymurrc -On windows, the path to the configuration file can be determined by starting up Python -and typing :: +On windows, the path to the configuration file can be determined +by starting up Python and typing :: import os os.path.join(os.path.expanduser('~'), 'glymur', 'glymurrc') @@ -75,9 +76,7 @@ MacPorts. You should install the following set of ports: * py33-matplotlib (optional, for running certain tests) * py33-Pillow (optional, for running certain tests) -MacPorts supplies both OpenJPEG 1.5.0 and OpenJPEG 2.0.0. As previously -mentioned, the 2.0.0 official release is not supported (although the 2.0+ -development version via SVN *is* supported). +MacPorts supplies both OpenJPEG 1.5.0 and OpenJPEG 2.0.0. Linux ----- diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 00d029e..0a59ebd 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -14,22 +14,21 @@ some very limited support for reading JPX metadata. For instance, **asoc** and **labl** boxes are recognized, so GMLJP2 metadata can be retrieved from such JPX files. -Glymur works on Python 2.6, 2.7, and 3.3. Python 3.3 is strongly recommended. +Glymur works on Python 2.6, 2.7, and 3.3. OpenJPEG Installation ===================== -OpenJPEG should be version 1.4, 1.5, or the trunk/development -version of OpenJPEG. The official 2.0.0 release or versions earlier than 1.3.0 -are not supported. Furthermore, the 1.X versions of OpenJPEG are -currently only utilized for read-only purposes. In order to write JPEG 2000 -images, you must compile the the trunk/development version. For more information -about OpenJPEG, please consult http://www.openjpeg.org. +Glymur will read JPEG 2000 images with versions 1.3, 1.4, 1.5, 2.0, +and the trunk/development version of OpenJPEG. Writing images is +only supported with the 2.0 series, however, and the trunk/development +version is strongly recommended. For more information about OpenJPEG, +please consult http://www.openjpeg.org. If you use MacPorts on the mac or if you have a sufficiently recent version of Linux, your package manager should already provide you with a version of OpenJPEG 1.X with which glymur can already use for read-only purposes. If your platform is windows, I suggest -using the 1.5.1 windows installer provided to you by the OpenJPEG +using the windows installers provided to you by the OpenJPEG folks at https://code.google.com/p/openjpeg/downloads/list . Glymur Installation @@ -57,5 +56,5 @@ You can run the tests from within python as follows:: >>> glymur.runtests() Many tests are currently skipped; in fact most of them are skipped if you -are relying on OpenJPEG 1.4 or 1.5. The important thing, though, is whether or +are relying on OpenJPEG 1.X. The important thing, though, is whether or not any tests fail. From 5a78d9ad8e27181be8ec163e49eaa5297d0a165a Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 29 Jul 2013 20:11:52 -0400 Subject: [PATCH 10/24] Added windows stanza for default openjp2 installation location. #90 --- glymur/lib/config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/glymur/lib/config.py b/glymur/lib/config.py index 72d91a9..89c596f 100644 --- a/glymur/lib/config.py +++ b/glymur/lib/config.py @@ -109,6 +109,11 @@ def load_openjp2(libopenjp2_path): path = '/opt/local/lib/libopenjp2.dylib' if os.path.exists(path): libopenjp2_path = path + elif os.name == 'nt': + path = os.path.join('C:\\', 'Program files', 'OpenJPEG 2.0', + 'bin', 'openjp2.dll') + if os.path.exists(path): + libopenjpeg_path = path if libopenjp2_path is None: return None From 3057d5ef7d4810ebb7b9d93164bd951d3f2dde8e Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 29 Jul 2013 20:12:46 -0400 Subject: [PATCH 11/24] Remove shutil test code, #90 --- glymur/test/test_jp2k.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 06ab05f..f0db4b7 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -694,8 +694,6 @@ 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) From 8f20232d0b5182b1b1d312707c786296532f6fb6 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 05:38:56 -0400 Subject: [PATCH 12/24] RGBA example would seem to require svn version. #90 --- docs/source/how_do_i.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/how_do_i.rst b/docs/source/how_do_i.rst index 9ffaee5..eee558c 100644 --- a/docs/source/how_do_i.rst +++ b/docs/source/how_do_i.rst @@ -122,10 +122,10 @@ and add it after the JP2 header box, but before the codestream box :: Create an image with an alpha layer? ==================================== -OpenJPEG can create JP2 files with more than 3 components, but by default, any -extra components are not described as such. In order to do so, -we need to rewrap such an image in a set of boxes that includes a channel -definition box. +OpenJPEG can create JP2 files with more than 3 components (requires +the development version), but by default, any extra components are +not described as such. In order to do so, we need to rewrap such +an image in a set of boxes that includes a channel definition box. This example is based on SciPy example code found at http://scipy-lectures.github.io/advanced/image_processing/#basic-manipulations . From 2a34561689d2ecd91dab4d948ab831a52384b73c Mon Sep 17 00:00:00 2001 From: John G Evans Date: Tue, 30 Jul 2013 08:18:13 -0400 Subject: [PATCH 13/24] Had wrong location for default windows openjp2 install. #90 --- glymur/lib/config.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/glymur/lib/config.py b/glymur/lib/config.py index 89c596f..e2b780a 100644 --- a/glymur/lib/config.py +++ b/glymur/lib/config.py @@ -75,8 +75,6 @@ def load_openjpeg(libopenjpeg_path): def read_config_file(): """ - We expect to not find openjp2 on the system path since the only version - that we currently care about is still in the svn trunk at openjpeg.org. We must use a configuration file that the user must write. """ lib = {'openjp2': None, 'openjpeg': None} @@ -113,7 +111,7 @@ def load_openjp2(libopenjp2_path): path = os.path.join('C:\\', 'Program files', 'OpenJPEG 2.0', 'bin', 'openjp2.dll') if os.path.exists(path): - libopenjpeg_path = path + libopenjp2_path = path if libopenjp2_path is None: return None From 8deef3355cd04789dee41b2572b8670e56f5b3d8 Mon Sep 17 00:00:00 2001 From: John G Evans Date: Tue, 30 Jul 2013 08:18:37 -0400 Subject: [PATCH 14/24] Added winpython 3.3 / openjpeg 2.0 note. --- docs/source/detailed_installation.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst index 84fe887..126efaa 100644 --- a/docs/source/detailed_installation.rst +++ b/docs/source/detailed_installation.rst @@ -137,12 +137,14 @@ In addition, you must install contextlib2 and Pillow via pip. :: Windows ------- -I would recommend using WinPython, but Python(xy) also seems to work. WinPython 3.3 -should work with no additional installations required, but 2.7 versions still require -contextlib2 and mock to be installed via pip. +32-bit WinPython 2.7.5 seems to work with OpenJPEG 1.X, 2.0, and the +development version, but still requires contextlib2 and mock to be +installed via pip. WinPython 3.3.2, however, seems to have trouble +with OpenJPEG 2.0, so I would suggest using the development version +there (I'm unwilling to spend ANY more time trying to figure out what +the problem is there). -Glymur has been tested far less extensively on Windows than on the other -platforms. +64-bit windows is completely untested. ''''''' From 0b5c8ce3701998f54ed4b858d9cc5d24b1862190 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 08:44:47 -0400 Subject: [PATCH 15/24] Added a known problems section, details winpython 3.3.2/opj 2.0 issue. Closes #90. --- docs/source/roadmap.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/source/roadmap.rst b/docs/source/roadmap.rst index f1434b2..7f50a87 100644 --- a/docs/source/roadmap.rst +++ b/docs/source/roadmap.rst @@ -1,11 +1,17 @@ +------------ +Known Issues +------------ + + * WinPython 3.3.2 and OpenJPEG 2.0 don't seem to want to play well together. If you do not need write support, just use OpenJPEG 1.5 instead. If you do need write support, try the development version of OpenJPEG. + ------- Roadmap ------- -Here's an incomplete list of what I'd like to focus on in the near future. +Here's an incomplete list of what I'd like to focus on in the future. * continue to monitor upstream changes in the openjp2 library * investigate using CFFI or cython instead of ctypes to wrap openjp2 - * eventually expose the openjp2 API - * investigate JPIP + * investigate adding write support for UUID/XMP boxes (potentially a big project) + * investigate JPIP (likely to be an even bigger project) From 35288ce73e44e1a4d78440c9d59029d4f97d350d Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 09:50:30 -0400 Subject: [PATCH 16/24] Bad predicate in unittest decorator for TestCallbacks15. #90 --- glymur/test/test_callbacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glymur/test/test_callbacks.py b/glymur/test/test_callbacks.py index d1a06c5..2d4a3de 100644 --- a/glymur/test/test_callbacks.py +++ b/glymur/test/test_callbacks.py @@ -66,7 +66,7 @@ class TestCallbacks(unittest.TestCase): self.assertEqual(actual, expected) -@unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None, +@unittest.skipIf(glymur.lib.openjpeg.OPENJPEG is None, "Missing openjpeg library.") class TestCallbacks15(unittest.TestCase): """This test suite is for OpenJPEG 1.5.1 properties. From 93270a0d501ad8261742b2af14929e607a9d11e2 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 09:51:40 -0400 Subject: [PATCH 17/24] Removed monkey-patching of the library version when not found. #90 --- glymur/lib/openjpeg.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/glymur/lib/openjpeg.py b/glymur/lib/openjpeg.py index 56bdfd3..704cf9c 100644 --- a/glymur/lib/openjpeg.py +++ b/glymur/lib/openjpeg.py @@ -28,9 +28,6 @@ else: # Does not really matter. But version should not be called if there is no # OpenJPEG library found. _MINOR = 0 - # Redefine version so that we can use it. - def version(): - return '0.0.0' class EventMgrType(ctypes.Structure): From 6581540f34004090ab40584248accc1808f1fb0f Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 09:52:07 -0400 Subject: [PATCH 18/24] Added monkey-patched fixture for openjpeg version. --- glymur/test/fixtures.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 3e5cfbb..39b4ac5 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -5,6 +5,14 @@ import numpy as np import glymur +# Need to know the openjpeg version. If openjpeg is not installed, we use +# '0.0.0' +OPENJPEG_VERSION = '0.0.0' +if glymur.lib.openjpeg.OPENJPEG is not None: + OPENJPEG_VERSION = glymur.lib.openjpeg.version() + +# Need to know of the libopenjp2 version is the official 2.0.0 release and NOT +# the 2.0+ development version. OPENJP2_IS_V2_OFFICIAL = False if glymur.lib.openjp2.OPENJP2 is not None: if not hasattr(glymur.lib.openjp2.OPENJP2, From 3f417a30b100c9eb9dc91150c67896b8a3dd4de8 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 09:52:53 -0400 Subject: [PATCH 19/24] Use monkey-patched openjpeg version when library not available. Closes #92 --- glymur/test/test_opj_suite.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index b42e033..b6de353 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -30,6 +30,7 @@ import numpy as np from glymur import Jp2k import glymur +from .fixtures import OPENJPEG_VERSION from .fixtures import OPENJP2_IS_V2_OFFICIAL from .fixtures import * @@ -7801,7 +7802,8 @@ class TestSuite15(unittest.TestCase): jfile = os.path.join(data_root, 'input/conformance/file9.jp2') jp2k = Jp2k(jfile) jpdata = jp2k.read() - if glymur.lib.openjpeg.version().startswith('1.3'): + if re.match('[01]\.3', OPENJPEG_VERSION): + # Version 1.3 reads in the image as the palette indices. self.assertEqual(jpdata.shape, (512, 768)) else: self.assertEqual(jpdata.shape, (512, 768, 3)) @@ -7840,7 +7842,7 @@ class TestSuite15(unittest.TestCase): data = jp2.read() self.assertTrue(True) - @unittest.skipIf(int(glymur.lib.openjpeg.version().split('.')[1]) < 5, + @unittest.skipIf(re.match('[01]\.[34]', OPENJPEG_VERSION), "Segfaults openjpeg 1.4 and earlier.") def test_NR_DEC_broken2_jp2_5_decode(self): # Null pointer access @@ -7863,7 +7865,7 @@ class TestSuite15(unittest.TestCase): with self.assertRaises(ValueError) as ce: d = j.read() - @unittest.skipIf(int(glymur.lib.openjpeg.version().split('.')[1]) < 5, + @unittest.skipIf(re.match('[01]\.[34]', OPENJPEG_VERSION), "Segfaults openjpeg 1.4 and earlier.") def test_NR_DEC_broken4_jp2_7_decode(self): # Null pointer access From 687d94d415ac68f040183c1800e454e8c7a26096 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 30 Jul 2013 12:07:33 -0400 Subject: [PATCH 20/24] Monkey-patching replaced with unittest.patch. Somewhat saner. closes #93 --- glymur/test/test_callbacks.py | 80 +++++++++++++++-------------------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/glymur/test/test_callbacks.py b/glymur/test/test_callbacks.py index 2d4a3de..47ec100 100644 --- a/glymur/test/test_callbacks.py +++ b/glymur/test/test_callbacks.py @@ -6,16 +6,18 @@ import sys import tempfile import warnings -if sys.hexversion < 0x03000000: - from StringIO import StringIO -else: - from io import StringIO - if sys.hexversion < 0x02070000: import unittest2 as unittest else: import unittest +if sys.hexversion <= 0x03030000: + from mock import patch + from StringIO import StringIO +else: + from unittest.mock import patch + from io import StringIO + import glymur @@ -24,15 +26,11 @@ import glymur class TestCallbacks(unittest.TestCase): 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 + pass @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_info_callback_on_write(self): @@ -43,8 +41,9 @@ class TestCallbacks(unittest.TestCase): tiledata = j.read(tile=0) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: j = glymur.Jp2k(tfile.name, 'wb') - j.write(tiledata, verbose=True) - actual = sys.stdout.getvalue().strip() + with patch('sys.stdout', new=StringIO()) as fake_out: + j.write(tiledata, verbose=True) + actual = fake_out.getvalue().strip() expected = '[INFO] tile number 1 / 1' self.assertEqual(actual, expected) @@ -52,8 +51,9 @@ class TestCallbacks(unittest.TestCase): # Verify that we get the expected stdio output when our internal info # callback handler is enabled. j = glymur.Jp2k(self.j2kfile) - d = j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150)) - actual = sys.stdout.getvalue().strip() + with patch('sys.stdout', new=StringIO()) as fake_out: + d = j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150)) + actual = fake_out.getvalue().strip() lines = ['[INFO] Start to read j2k main header (0).', '[INFO] Main header has been correctly decoded.', @@ -72,47 +72,35 @@ class TestCallbacks15(unittest.TestCase): """This test suite is for OpenJPEG 1.5.1 properties. """ - @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 + pass 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(rlevel=1, verbose=True) - actual = sys.stdout.getvalue().strip() - - regex = re.compile(r"""\[INFO\]\stile\s1\sof\s1\s+ - \[INFO\]\s-\stiers-1\stook\s - [0-9]+\.[0-9]+\ss\s+ - \[INFO\]\s-\sdwt\stook\s - (-){0,1}[0-9]+\.[0-9]+\ss\s+ - \[INFO\]\s-\stile\sdecoded\sin\s - [0-9]+\.[0-9]+\ss""", - re.VERBOSE) - if sys.hexversion <= 0x03020000: - self.assertRegexpMatches(actual, regex) - else: - self.assertRegex(actual, regex) + with patch('glymur.lib.openjp2.OPENJP2', new=None): + # Force to use OPENJPEG instead of OPENJP2. + j = glymur.Jp2k(self.j2kfile) + with patch('sys.stdout', new=StringIO()) as fake_out: + d = j.read(rlevel=1, verbose=True) + actual = fake_out.getvalue().strip() + + regex = re.compile(r"""\[INFO\]\stile\s1\sof\s1\s+ + \[INFO\]\s-\stiers-1\stook\s + [0-9]+\.[0-9]+\ss\s+ + \[INFO\]\s-\sdwt\stook\s + (-){0,1}[0-9]+\.[0-9]+\ss\s+ + \[INFO\]\s-\stile\sdecoded\sin\s + [0-9]+\.[0-9]+\ss""", + re.VERBOSE) + if sys.hexversion <= 0x03020000: + self.assertRegexpMatches(actual, regex) + else: + self.assertRegex(actual, regex) if __name__ == "__main__": From e3400d26e7b29c47d0022c456ab3343d219b1938 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 31 Jul 2013 06:42:27 -0400 Subject: [PATCH 21/24] Updating release 0.3.0 test information. --- release.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/release.txt b/release.txt index 7504f7f..73fc0b3 100644 --- a/release.txt +++ b/release.txt @@ -9,24 +9,36 @@ | 10.6.8 | | | | and OpenJPEG svn. 354 of 455 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ +| Mac | X | | | MacPorts with both OpenJPEG 1.5.1 | +| 10.6.8 | | | | and OpenJPEG 2.0. 315 of 456 tests | +| | | | | should pass. | ++-----------+--------+--------+--------+--------------------------------------+ | Mac | | X | | MacPorts with both OpenJPEG 1.5.1 | | 10.6.8 | | | | and OpenJPEG svn. 379 of 460 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ +| Mac | | X | | MacPorts with both OpenJPEG 1.5.1 | +| 10.6.8 | | | | and OpenJPEG 2.0. 340 of 461 tests | +| | | | | should pass. | ++-----------+--------+--------+--------+--------------------------------------+ | Mac | | | X | MacPorts with both OpenJPEG 1.5.1 | | 10.6.8 | | | | and OpenJPEG svn. 407 of 460 | | | | | | tests should pass. | ++-----------+--------+--------+--------+--------------------------------------+ +| Mac | | | X | MacPorts with both OpenJPEG 1.5.1 | +| 10.6.8 | | | | and OpenJPEG 2.0. 355 of 461 | +| | | | | tests should pass. | +-----------+--------+--------+-----------------------------------------------+ | Fedora 19 | | | X | Ships with 1.5.1, openjp2 built too. | | | | | | 402 of 455 tests should pass. | +-----------+--------+--------+--------+--------------------------------------+ -| Fedora 18 | | | X | Ships with 1.5.1. 169 of 449 tests | +| Fedora 18 | | | X | Ships with 1.5.1. 173 of 456 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ -| Fedora 17 | | X | | Ships with 1.4.0. 166 of 450 tests | +| Fedora 17 | | X | | Ships with 1.4.0. 171 of 456 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ -| CentOS | X | | | Ships with 1.3.0. 169 of 455 tests | +| CentOS | X | | | Ships with 1.3.0. 169 of 456 tests | | 6.3 | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Raspberry | | X | | Ships with 1.3.0. 171 of 455 tests | From 4c742c0aaab32244e54b79f251389fe9852fd3ee Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 31 Jul 2013 06:44:45 -0400 Subject: [PATCH 22/24] Prepping for 0.3.0 release. --- CHANGES.txt | 2 ++ docs/source/conf.py | 2 +- setup.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b6441a8..42b5b4b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +Jul 31, 2013 - v0.3.0 Added support for official 2.0.0. + Jul 27, 2013 - v0.2.8 Fixed inconsistency regarding configuration file directory on windows (issue91). diff --git a/docs/source/conf.py b/docs/source/conf.py index 0225ce8..79e33af 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -78,7 +78,7 @@ copyright = u'2013, John Evans' # The short X.Y version. version = '0.2' # The full version, including alpha/beta/rc tags. -release = '0.2.8' +release = '0.3.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index b611908..3f72b75 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages import sys kwargs = {'name': 'Glymur', - 'version': '0.2.8', + 'version': '0.3.0rc1', 'description': 'Tools for accessing JPEG2000 files', 'long_description': open('README.md').read(), 'author': 'John Evans', From 34b228b7f4595143d4ea4e07acc2421c5fcc3851 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 31 Jul 2013 08:13:00 -0400 Subject: [PATCH 23/24] Updating release matrix for 0.3.0 --- release.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/release.txt b/release.txt index 73fc0b3..2b4ecce 100644 --- a/release.txt +++ b/release.txt @@ -1,8 +1,12 @@ | OS | Python | Python | Python | Notes | | | 2.6 | 2.7 | 3.3 | | +-----------+--------+--------+--------+--------------------------------------+ -| Windows | | X | | Python(xy) with OpenJPEG 1.5.1 and | -| | | | | OpenJPEG svn. 285 of 448 tests | +| Windows | | X | | WinPython with OpenJPEG 1.5.1 and | +| | | | | OpenJPEG 2.0.0. 267 of 455 tests | +| | | | | pass. | ++-----------+--------+--------+--------+--------------------------------------+ +| Windows | | | X | WinPython with OpenJPEG 1.5.1 and | +| | | | | OpenJPEG svn. 307 of 455 tests | | | | | | pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | X | | | MacPorts with both OpenJPEG 1.5.1 | @@ -29,10 +33,10 @@ | 10.6.8 | | | | and OpenJPEG 2.0. 355 of 461 | | | | | | tests should pass. | +-----------+--------+--------+-----------------------------------------------+ -| Fedora 19 | | | X | Ships with 1.5.1, openjp2 built too. | -| | | | | 402 of 455 tests should pass. | +| Fedora 19 | | | X | Ships with 1.5.1, openjp2 svn built, | +| | | | | too. 407 of 461 tests should pass. | +-----------+--------+--------+--------+--------------------------------------+ -| Fedora 18 | | | X | Ships with 1.5.1. 173 of 456 tests | +| Fedora 18 | | X | | Ships with 1.5.1. 173 of 456 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Fedora 17 | | X | | Ships with 1.4.0. 171 of 456 tests | From 6af303263653b899b641de2ac245f52de24ac222 Mon Sep 17 00:00:00 2001 From: jevans Date: Wed, 31 Jul 2013 18:52:00 -0400 Subject: [PATCH 24/24] Final bump for 0.3.0. --- release.txt | 8 ++++---- setup.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/release.txt b/release.txt index 2b4ecce..17d55e0 100644 --- a/release.txt +++ b/release.txt @@ -10,7 +10,7 @@ | | | | | pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | X | | | MacPorts with both OpenJPEG 1.5.1 | -| 10.6.8 | | | | and OpenJPEG svn. 354 of 455 tests | +| 10.6.8 | | | | and OpenJPEG svn. 354 of 456 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | X | | | MacPorts with both OpenJPEG 1.5.1 | @@ -18,7 +18,7 @@ | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | | X | | MacPorts with both OpenJPEG 1.5.1 | -| 10.6.8 | | | | and OpenJPEG svn. 379 of 460 tests | +| 10.6.8 | | | | and OpenJPEG svn. 379 of 461 tests | | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | | X | | MacPorts with both OpenJPEG 1.5.1 | @@ -26,7 +26,7 @@ | | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | | | X | MacPorts with both OpenJPEG 1.5.1 | -| 10.6.8 | | | | and OpenJPEG svn. 407 of 460 | +| 10.6.8 | | | | and OpenJPEG svn. 407 of 461 | | | | | | tests should pass. | +-----------+--------+--------+--------+--------------------------------------+ | Mac | | | X | MacPorts with both OpenJPEG 1.5.1 | @@ -45,7 +45,7 @@ | CentOS | X | | | Ships with 1.3.0. 169 of 456 tests | | 6.3 | | | | should pass. | +-----------+--------+--------+--------+--------------------------------------+ -| Raspberry | | X | | Ships with 1.3.0. 171 of 455 tests | +| Raspberry | | X | | Ships with 1.3.0. 171 of 456 tests | | Pi | | | | should pass. | | Debian 7 | | | | | +-----------+--------+--------+--------+--------------------------------------+ diff --git a/setup.py b/setup.py index 3f72b75..f725631 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages import sys kwargs = {'name': 'Glymur', - 'version': '0.3.0rc1', + 'version': '0.3.0', 'description': 'Tools for accessing JPEG2000 files', 'long_description': open('README.md').read(), 'author': 'John Evans',