From 008048b25ecaa0c42b1fe51b958a43bee3bc18ae Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 23 Sep 2013 12:33:05 -0400 Subject: [PATCH 01/22] Removed some "skipIf" conditions that no loger seem relevant. #121 --- glymur/test/test_jp2box.py | 8 -------- glymur/test/test_jp2k.py | 2 -- glymur/test/test_opj_suite_neg.py | 7 +++---- glymur/test/test_printing.py | 4 ++-- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 71dbd72..0d265b0 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -57,11 +57,7 @@ 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.") class TestChannelDefinition(unittest.TestCase): """Test suite for channel definition boxes.""" @@ -505,8 +501,6 @@ class TestColourSpecificationBox(unittest.TestCase): approximation=approx) -@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, - "Missing openjp2 library.") class TestAppend(unittest.TestCase): """Tests for append method.""" @@ -599,8 +593,6 @@ class TestAppend(unittest.TestCase): jp2.append(uuidbox) -@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, - "Missing openjp2 library.") class TestWrap(unittest.TestCase): """Tests for wrap method.""" diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index bbf551b..b6390c4 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -56,8 +56,6 @@ def load_tests(loader, tests, ignore): @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") -@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, - "Missing openjp2 library.") class TestJp2kBadXmlFile(unittest.TestCase): """Test suite for bad XML box situations""" diff --git a/glymur/test/test_opj_suite_neg.py b/glymur/test/test_opj_suite_neg.py index 66115a2..2e7e3b2 100644 --- a/glymur/test/test_opj_suite_neg.py +++ b/glymur/test/test_opj_suite_neg.py @@ -12,6 +12,7 @@ seem like logical negative tests to add. # pylint: disable=F0401 import os +import re import sys import tempfile @@ -22,16 +23,14 @@ else: import numpy as np -from .fixtures import read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG from .fixtures import OPJ_DATA_ROOT, opj_data_file from glymur import Jp2k import glymur -@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, - "Missing openjp2 library.") -@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG) +@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version), + "Functionality not implemented for 1.3, 1.4") @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_OPJ_DATA_ROOT environment variable not set") class TestSuiteNegative(unittest.TestCase): diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 8591b1b..81f998e 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -36,8 +36,8 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") -@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, - "Missing openjp2 library.") +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 0, + "Missing library.") class TestPrintingNeedsLib(unittest.TestCase): """These tests require the library, mostly in order to just setup the test. """ From bf9833550452b29b3e29e5ee3b91d4df95c4b3e3 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 23 Sep 2013 16:09:01 -0400 Subject: [PATCH 02/22] Fixed some tests failing in 2.0 official. --- glymur/test/test_jp2box.py | 3 +++ glymur/test/test_jp2k.py | 5 +++-- glymur/test/test_opj_suite.py | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 0d265b0..15aed61 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -57,6 +57,9 @@ def load_tests(loader, tests, ignore): return tests +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] > 1 and + OPENJP2_IS_V2_OFFICIAL, + "Not supported until 2.0+.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestChannelDefinition(unittest.TestCase): """Test suite for channel definition boxes.""" diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index b6390c4..394f9e5 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -117,7 +117,8 @@ class TestJp2kBadXmlFile(unittest.TestCase): self.assertIsNone(jp2k.box[3].xml) -@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and not OPENJP2_IS_V2_OFFICIAL, +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] > 1 and + OPENJP2_IS_V2_OFFICIAL, "Missing openjp2 library version 2.0+.") class TestJp2k_2_1(unittest.TestCase): """Test suite for version 2.0+ of openjpeg software""" @@ -201,7 +202,7 @@ class TestJp2k_2_0(unittest.TestCase): def tearDown(self): pass - @unittest.skipIf(not OPENJP2_IS_V2_OFFICIAL, + @unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, "Behavior is specific to 2.0 official.") @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_extra_components_on_v2(self): diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index a5daf3e..3543e01 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -6724,6 +6724,8 @@ class TestSuiteDump(unittest.TestCase): [8, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10]) +@unittest.skipIf(OPJ_DATA_ROOT is None, + "OPJ_DATA_ROOT environment variable not set") @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1, "Feature not supported in glymur until openjpeg 2.0") class TestSuite_bands(unittest.TestCase): @@ -6853,6 +6855,8 @@ class TestSuite_bands(unittest.TestCase): self.assertTrue(True) +@unittest.skipIf(OPJ_DATA_ROOT is None, + "OPJ_DATA_ROOT environment variable not set") @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1, "Tests not passing until 2.0") class TestSuite2point0(unittest.TestCase): From e20cf07a8a6f50c325f2ca60e81f28e5db43e2db Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 23 Sep 2013 16:22:49 -0400 Subject: [PATCH 03/22] Requalified on 1.3. #121 --- glymur/test/test_jp2box.py | 2 +- glymur/test/test_jp2k.py | 2 +- glymur/test/test_printing.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 15aed61..ff69f75 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -57,7 +57,7 @@ def load_tests(loader, tests, ignore): return tests -@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] > 1 and +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2 or OPENJP2_IS_V2_OFFICIAL, "Not supported until 2.0+.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 394f9e5..ed78870 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -117,7 +117,7 @@ class TestJp2kBadXmlFile(unittest.TestCase): self.assertIsNone(jp2k.box[3].xml) -@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] > 1 and +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2 or OPENJP2_IS_V2_OFFICIAL, "Missing openjp2 library version 2.0+.") class TestJp2k_2_1(unittest.TestCase): diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 81f998e..2694aa2 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -10,6 +10,7 @@ # pylint: disable=R0904 import os +import re import struct import sys import tempfile @@ -36,8 +37,8 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") -@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 0, - "Missing library.") +@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version), + "Need at least 1.5 in order to write jp2 files.") class TestPrintingNeedsLib(unittest.TestCase): """These tests require the library, mostly in order to just setup the test. """ From c7372552721454f101a7fefc0a55307f18d190e9 Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 23 Sep 2013 20:17:15 -0400 Subject: [PATCH 04/22] Releasing 0.5.2 --- CHANGES.txt | 3 +++ docs/source/conf.py | 2 +- glymur/version.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 03d3b94..7d437d3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +Sep 23, 2013 - v0.5.2 Fixed some teests that have been failing since 0.5. + under various edge cases. + Sep 19, 2013 - v0.5.1 Added more resiliency to XML box parsing. Fixed tests that failed if OPJ_DATA_ROOT not set. diff --git a/docs/source/conf.py b/docs/source/conf.py index 25d944f..75404f9 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.5' # The full version, including alpha/beta/rc tags. -release = '0.5.1' +release = '0.5.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/glymur/version.py b/glymur/version.py index 8b1eb50..4386ba6 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -13,7 +13,7 @@ from distutils.version import LooseVersion from .lib import openjpeg as opj from .lib import openjp2 as opj2 -version = "0.5.1" +version = "0.5.2" _sv = LooseVersion(version) version_tuple = _sv.version From 275e0fbd6bc166d61a7e6d2a7a79c2a509a672e4 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 24 Sep 2013 10:00:17 -0400 Subject: [PATCH 05/22] Bad test suite removed. Closes #122 The channel definition test suite looks like it was mistakenly pasted into another part of the file but not correctly skipped when the library version was 1.3. --- glymur/test/test_jp2box.py | 260 ------------------------------------- 1 file changed, 260 deletions(-) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index bbdbb30..ef62460 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -56,7 +56,6 @@ def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite('glymur.jp2box')) return tests - @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2 or OPENJP2_IS_V2_OFFICIAL, "Not supported until 2.0+.") @@ -744,264 +743,5 @@ class TestJpxBoxes(unittest.TestCase): self.assertEqual(len(jpx.box[5].box), 0) -@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") -class TestChannelDefinition(unittest.TestCase): - """Test suite for channel definition boxes.""" - - @classmethod - def setUpClass(cls): - """Need a one_plane plane image for greyscale testing.""" - j2k = Jp2k(glymur.data.goodstuff()) - data = j2k.read() - # Write the first component back out to file. - with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: - grey_j2k = Jp2k(tfile.name, 'wb') - grey_j2k.write(data[:, :, 0]) - cls.one_plane = tfile.name - # Write the first two components back out to file. - with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: - grey_j2k = Jp2k(tfile.name, 'wb') - grey_j2k.write(data[:, :, 0:1]) - cls.two_planes = tfile.name - # Write four components back out to file. - with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: - rgba_jp2 = Jp2k(tfile.name, 'wb') - shape = (data.shape[0], data.shape[1], 1) - alpha = np.zeros((shape), dtype=data.dtype) - data4 = np.concatenate((data, alpha), axis=2) - rgba_jp2.write(data4) - cls.four_planes = tfile.name - - @classmethod - def tearDownClass(cls): - os.unlink(cls.one_plane) - os.unlink(cls.two_planes) - os.unlink(cls.four_planes) - - def setUp(self): - self.jp2file = glymur.data.nemo() - self.j2kfile = glymur.data.goodstuff() - - j2k = Jp2k(self.j2kfile) - codestream = j2k.get_codestream() - height = codestream.segment[1].ysiz - width = codestream.segment[1].xsiz - num_components = len(codestream.segment[1].xrsiz) - - self.jp2b = JPEG2000SignatureBox() - self.ftyp = FileTypeBox() - self.jp2h = JP2HeaderBox() - self.jp2c = ContiguousCodestreamBox() - self.ihdr = ImageHeaderBox(height=height, width=width, - num_components=num_components) - self.colr_rgb = ColourSpecificationBox(colorspace=glymur.core.SRGB) - self.colr_gr = ColourSpecificationBox(colorspace=glymur.core.GREYSCALE) - - def tearDown(self): - pass - - def test_cdef_no_inputs(self): - """channel_type and association are required inputs.""" - with self.assertRaises(IOError): - glymur.jp2box.ChannelDefinitionBox() - - def test_rgb_with_index(self): - """Just regular RGB.""" - j2k = Jp2k(self.j2kfile) - channel_type = [COLOR, COLOR, COLOR] - association = [RED, GREEN, BLUE] - cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], - channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_rgb, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - j2k.wrap(tfile.name, boxes=boxes) - - jp2 = Jp2k(tfile.name) - jp2h = jp2.box[2] - boxes = [box.box_id for box in jp2h.box] - self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) - self.assertEqual(jp2h.box[2].index, (0, 1, 2)) - self.assertEqual(jp2h.box[2].channel_type, - (COLOR, COLOR, COLOR)) - self.assertEqual(jp2h.box[2].association, - (RED, GREEN, BLUE)) - - def test_rgb(self): - """Just regular RGB, but don't supply the optional index.""" - j2k = Jp2k(self.j2kfile) - channel_type = [COLOR, COLOR, COLOR] - association = [RED, GREEN, BLUE] - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_rgb, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - j2k.wrap(tfile.name, boxes=boxes) - - jp2 = Jp2k(tfile.name) - jp2h = jp2.box[2] - boxes = [box.box_id for box in jp2h.box] - self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) - self.assertEqual(jp2h.box[2].index, (0, 1, 2)) - self.assertEqual(jp2h.box[2].channel_type, - (COLOR, COLOR, COLOR)) - self.assertEqual(jp2h.box[2].association, - (RED, GREEN, BLUE)) - - def test_rgba(self): - """Just regular RGBA.""" - j2k = Jp2k(self.four_planes) - channel_type = (COLOR, COLOR, COLOR, OPACITY) - association = (RED, GREEN, BLUE, WHOLE_IMAGE) - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_rgb, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - j2k.wrap(tfile.name, boxes=boxes) - - jp2 = Jp2k(tfile.name) - jp2h = jp2.box[2] - boxes = [box.box_id for box in jp2h.box] - self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) - self.assertEqual(jp2h.box[2].index, (0, 1, 2, 3)) - self.assertEqual(jp2h.box[2].channel_type, channel_type) - self.assertEqual(jp2h.box[2].association, association) - - def test_bad_rgba(self): - """R, G, and B must be specified.""" - j2k = Jp2k(self.four_planes) - channel_type = (COLOR, COLOR, OPACITY, OPACITY) - association = (RED, GREEN, BLUE, WHOLE_IMAGE) - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_rgb, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - with self.assertRaises(IOError): - j2k.wrap(tfile.name, boxes=boxes) - - def test_grey(self): - """Just regular greyscale.""" - j2k = Jp2k(self.one_plane) - channel_type = (COLOR,) - association = (GREY,) - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_gr, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - j2k.wrap(tfile.name, boxes=boxes) - - jp2 = Jp2k(tfile.name) - jp2h = jp2.box[2] - boxes = [box.box_id for box in jp2h.box] - self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) - self.assertEqual(jp2h.box[2].index, (0,)) - self.assertEqual(jp2h.box[2].channel_type, channel_type) - self.assertEqual(jp2h.box[2].association, association) - - def test_grey_alpha(self): - """Just regular greyscale plus alpha.""" - j2k = Jp2k(self.two_planes) - channel_type = (COLOR, OPACITY) - association = (GREY, WHOLE_IMAGE) - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_gr, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - j2k.wrap(tfile.name, boxes=boxes) - - jp2 = Jp2k(tfile.name) - jp2h = jp2.box[2] - boxes = [box.box_id for box in jp2h.box] - self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) - self.assertEqual(jp2h.box[2].index, (0, 1)) - self.assertEqual(jp2h.box[2].channel_type, channel_type) - self.assertEqual(jp2h.box[2].association, association) - - def test_bad_grey_alpha(self): - """A greyscale image with alpha layer must specify a color channel""" - j2k = Jp2k(self.two_planes) - - channel_type = (OPACITY, OPACITY) - association = (GREY, WHOLE_IMAGE) - - # This cdef box - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - boxes = [self.ihdr, self.colr_gr, cdef] - self.jp2h.box = boxes - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - with self.assertRaises((OSError, IOError)): - j2k.wrap(tfile.name, boxes=boxes) - - def test_only_one_cdef_in_jp2h(self): - """There can only be one channel definition box in the jp2 header.""" - j2k = Jp2k(self.j2kfile) - - channel_type = (COLOR, COLOR, COLOR) - association = (RED, GREEN, BLUE) - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - - boxes = [self.ihdr, cdef, self.colr_rgb, cdef] - self.jp2h.box = boxes - - boxes = [self.jp2b, self.ftyp, self.jp2h, self.jp2c] - - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - with self.assertRaises(IOError): - j2k.wrap(tfile.name, boxes=boxes) - - def test_not_in_jp2h(self): - """need cdef in jp2h""" - j2k = Jp2k(self.j2kfile) - boxes = [self.ihdr, self.colr_rgb] - self.jp2h.box = boxes - - channel_type = (COLOR, COLOR, COLOR) - association = (RED, GREEN, BLUE) - cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - - boxes = [self.jp2b, self.ftyp, self.jp2h, cdef, self.jp2c] - - with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: - with self.assertRaises(IOError): - j2k.wrap(tfile.name, boxes=boxes) - - def test_bad_type(self): - """Channel types are limited to 0, 1, 2, 65535 - Should reject if not all of index, channel_type, association the - same length. - """ - channel_type = (COLOR, COLOR, 3) - association = (RED, GREEN, BLUE) - with self.assertRaises(IOError): - glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - - def test_wrong_lengths(self): - """Should reject if not all of index, channel_type, association the - same length. - """ - channel_type = (COLOR, COLOR) - association = (RED, GREEN, BLUE) - with self.assertRaises(IOError): - glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, - association=association) - - if __name__ == "__main__": unittest.main() From 6eba0a579463fbb1fb12a3880728f8aef07c6fc4 Mon Sep 17 00:00:00 2001 From: jevans Date: Tue, 24 Sep 2013 18:24:55 -0400 Subject: [PATCH 06/22] Prepping 0.5.3 release. --- CHANGES.txt | 3 +++ docs/source/conf.py | 2 +- glymur/version.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7d437d3..838f4a3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +Sep 24, 2013 - v0.5.3 Removed a duplicated channel definition test in + test_jp2box that could cause a segfault in 1.3 if not properly skipped. + Sep 23, 2013 - v0.5.2 Fixed some teests that have been failing since 0.5. under various edge cases. diff --git a/docs/source/conf.py b/docs/source/conf.py index 75404f9..78eb705 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.5' # The full version, including alpha/beta/rc tags. -release = '0.5.2' +release = '0.5.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/glymur/version.py b/glymur/version.py index 4386ba6..5a2fe1e 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -13,7 +13,7 @@ from distutils.version import LooseVersion from .lib import openjpeg as opj from .lib import openjp2 as opj2 -version = "0.5.2" +version = "0.5.3" _sv = LooseVersion(version) version_tuple = _sv.version From b1edfbe29621cf9cfd9ac0e92f1e6fe4d9390c27 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 25 Sep 2013 10:34:16 -0400 Subject: [PATCH 07/22] When working with 3.3, really should catch both IOError and OSError. On 2.7, the mac seems to issue an IOError, other platforms an OSError. Just catch both. #123 --- glymur/test/test_jp2k.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 2519246..ec174bf 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -146,7 +146,7 @@ class TestJp2k_2_0(unittest.TestCase): def test_extra_components_on_v2(self): """must error out in 1.x with extra components.""" # Extra components seems to require 2.0+. Verify that we error out. - with self.assertRaises(OSError): + with self.assertRaises((IOError, OSError)): with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: j = Jp2k(tfile.name, 'wb') data = np.zeros((128, 128, 4), dtype=np.uint8) From b50f19bed32c467d64a2701c6d941d8df2452c4c Mon Sep 17 00:00:00 2001 From: jevans Date: Wed, 25 Sep 2013 20:55:40 -0400 Subject: [PATCH 08/22] Prepping 0.5.4 release. --- CHANGES.txt | 2 ++ docs/source/conf.py | 2 +- glymur/version.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 838f4a3..d0e2bce 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +Sep 24, 2013 - v0.5.4 Fixed test error restricted to v2.0. + Sep 24, 2013 - v0.5.3 Removed a duplicated channel definition test in test_jp2box that could cause a segfault in 1.3 if not properly skipped. diff --git a/docs/source/conf.py b/docs/source/conf.py index 78eb705..e01cc91 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.5' # The full version, including alpha/beta/rc tags. -release = '0.5.3' +release = '0.5.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/glymur/version.py b/glymur/version.py index 5a2fe1e..5e4122b 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -13,7 +13,7 @@ from distutils.version import LooseVersion from .lib import openjpeg as opj from .lib import openjp2 as opj2 -version = "0.5.3" +version = "0.5.4" _sv = LooseVersion(version) version_tuple = _sv.version From 8cc04b2651439bf13934bebc1cd4f58b06f5db63 Mon Sep 17 00:00:00 2001 From: John Evans Date: Sat, 28 Sep 2013 11:50:27 -0400 Subject: [PATCH 09/22] Updated docs for openjpeg r2347. --- docs/source/detailed_installation.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst index 7fb5b3b..3f42275 100644 --- a/docs/source/detailed_installation.rst +++ b/docs/source/detailed_installation.rst @@ -13,7 +13,7 @@ both read and write JPEG 2000 files, but you may wish to install version 2.0 or the 2.0+ version from OpenJPEG's development trunk for better performance. If you do that, you should compile it 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. +via subversion. As of this time of writing, svn revision 2347 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 @@ -25,9 +25,10 @@ of running Glymur's test suite. :: Glymur uses ctypes to access the openjp2/openjpeg libraries, and because ctypes accesses libraries in a platform-dependent manner, it is recommended that you create a configuration file to help Glymur properly find -the openjpeg or openjp2 libraries (linux users don't need to bother if you are -using OpenJPEG as provided by your package manager). The configuration -format is the same as used by Python's configparser module, i.e. :: +the openjpeg or openjp2 libraries (linux users don't need to bother with this +if you are using OpenJPEG as provided by your package manager). The +configuration format is the same as used by Python's configparser module, +i.e. :: [library] openjp2: /opt/openjp2-svn/lib/libopenjp2.so From 813f5f3a03ac6a108c0e5781b5c1e43a4e11940a Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 30 Sep 2013 14:30:45 -0400 Subject: [PATCH 10/22] Moved argtypes and restype into each respective function. #125 --- glymur/lib/openjp2.py | 251 +++++++++++++++++++++--------------------- 1 file changed, 125 insertions(+), 126 deletions(-) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index 8c0ae32..43e4e61 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -553,119 +553,6 @@ class CodestreamInfoV2(ctypes.Structure): # information regarding tiles inside of image ("tile_info", ctypes.POINTER(TileInfoV2))] -# Restrict the input and output argument types for each function used in the -# API. -if OPENJP2 is not None: - OPENJP2.opj_create_compress.restype = CODEC_TYPE - OPENJP2.opj_create_compress.argtypes = [CODEC_FORMAT_TYPE] - - OPENJP2.opj_create_decompress.argtypes = [CODEC_FORMAT_TYPE] - OPENJP2.opj_create_decompress.restype = CODEC_TYPE - - ARGTYPES = [CODEC_TYPE, STREAM_TYPE_P, ctypes.POINTER(ImageType)] - OPENJP2.opj_decode.argtypes = ARGTYPES - - ARGTYPES = [CODEC_TYPE, ctypes.c_uint32, - ctypes.POINTER(ctypes.c_uint8), - ctypes.c_uint32, - STREAM_TYPE_P] - OPENJP2.opj_decode_tile_data.argtypes = ARGTYPES - - ARGTYPES = [ctypes.POINTER(ctypes.POINTER(CodestreamInfoV2))] - OPENJP2.opj_destroy_cstr_info.argtypes = ARGTYPES - OPENJP2.opj_destroy_cstr_info.restype = ctypes.c_void_p - - ARGTYPES = [CODEC_TYPE, STREAM_TYPE_P] - OPENJP2.opj_encode.argtypes = ARGTYPES - - OPENJP2.opj_get_cstr_info.argtypes = [CODEC_TYPE] - OPENJP2.opj_get_cstr_info.restype = ctypes.POINTER(CodestreamInfoV2) - - ARGTYPES = [CODEC_TYPE, - STREAM_TYPE_P, - ctypes.POINTER(ImageType), - ctypes.c_uint32] - OPENJP2.opj_get_decoded_tile.argtypes = ARGTYPES - - ARGTYPES = [ctypes.c_uint32, - ctypes.POINTER(ImageComptParmType), - COLOR_SPACE_TYPE] - OPENJP2.opj_image_create.argtypes = ARGTYPES - OPENJP2.opj_image_create.restype = ctypes.POINTER(ImageType) - - ARGTYPES = [ctypes.c_uint32, - ctypes.POINTER(ImageComptParmType), - COLOR_SPACE_TYPE] - OPENJP2.opj_image_tile_create.argtypes = ARGTYPES - OPENJP2.opj_image_tile_create.restype = ctypes.POINTER(ImageType) - - OPENJP2.opj_image_destroy.argtypes = [ctypes.POINTER(ImageType)] - - ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE, - ctypes.POINTER(ctypes.POINTER(ImageType))] - OPENJP2.opj_read_header.argtypes = ARGTYPES - - ARGTYPES = [CODEC_TYPE, - STREAM_TYPE_P, - ctypes.POINTER(ctypes.c_uint32), - ctypes.POINTER(ctypes.c_uint32), - ctypes.POINTER(ctypes.c_int32), - ctypes.POINTER(ctypes.c_int32), - ctypes.POINTER(ctypes.c_int32), - ctypes.POINTER(ctypes.c_int32), - ctypes.POINTER(ctypes.c_uint32), - ctypes.POINTER(BOOL_TYPE)] - OPENJP2.opj_read_tile_header.argtypes = ARGTYPES - - ARGTYPES = [CODEC_TYPE, ctypes.POINTER(ImageType), ctypes.c_int32, - ctypes.c_int32, ctypes.c_int32, ctypes.c_int32] - OPENJP2.opj_set_decode_area.argtypes = ARGTYPES - - ARGTYPES = [ctypes.POINTER(CompressionParametersType)] - OPENJP2.opj_set_default_encoder_parameters.argtypes = ARGTYPES - - ARGTYPES = [ctypes.POINTER(DecompressionParametersType)] - OPENJP2.opj_set_default_decoder_parameters.argtypes = ARGTYPES - - ARGTYPES = [CODEC_TYPE, ctypes.c_void_p, ctypes.c_void_p] - OPENJP2.opj_set_error_handler.argtypes = ARGTYPES - OPENJP2.opj_set_info_handler.argtypes = ARGTYPES - OPENJP2.opj_set_warning_handler.argtypes = ARGTYPES - - ARGTYPES = [CODEC_TYPE, ctypes.POINTER(DecompressionParametersType)] - OPENJP2.opj_setup_decoder.argtypes = ARGTYPES - - ARGTYPES = [CODEC_TYPE, - ctypes.POINTER(CompressionParametersType), - ctypes.POINTER(ImageType)] - OPENJP2.opj_setup_encoder.argtypes = ARGTYPES - - 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 - - OPENJP2.opj_end_compress.argtypes = [CODEC_TYPE, STREAM_TYPE_P] - OPENJP2.opj_end_decompress.argtypes = [CODEC_TYPE, STREAM_TYPE_P] - - OPENJP2.opj_destroy_codec.argtypes = [CODEC_TYPE] - - ARGTYPES = [CODEC_TYPE, - ctypes.c_uint32, - ctypes.POINTER(ctypes.c_uint8), - ctypes.c_uint32, - STREAM_TYPE_P] - OPENJP2.opj_write_tile.argtypes = ARGTYPES - def check_error(status): """Set a generic function as the restype attribute of all OpenJPEG @@ -684,19 +571,6 @@ def check_error(status): else: raise IOError("OpenJPEG function failure.") -# These library functions all return an error status. Circumvent that and -# force them to raise an exception. -FCNS = ['opj_decode', 'opj_decode_tile_data', 'opj_end_compress', - 'opj_encode', 'opj_end_decompress', 'opj_get_decoded_tile', - 'opj_read_header', 'opj_read_tile_header', 'opj_set_decode_area', - 'opj_set_error_handler', 'opj_set_info_handler', - 'opj_set_warning_handler', - 'opj_setup_decoder', 'opj_setup_encoder', 'opj_start_compress', - 'opj_write_tile'] -if OPENJP2 is not None: - for fcn in FCNS: - setattr(getattr(OPENJP2, fcn), 'restype', check_error) - def create_compress(codec_format): """Creates a J2K/JP2 compress structure. @@ -712,6 +586,9 @@ def create_compress(codec_format): ------- codec : Reference to CODEC_TYPE instance. """ + OPENJP2.opj_create_compress.restype = CODEC_TYPE + OPENJP2.opj_create_compress.argtypes = [CODEC_FORMAT_TYPE] + codec = OPENJP2.opj_create_compress(codec_format) return codec @@ -735,6 +612,10 @@ def decode(codec, stream, image): RuntimeError If the OpenJPEG library routine opj_decode fails. """ + OPENJP2.opj_decode.argtypes = [CODEC_TYPE, STREAM_TYPE_P, + ctypes.POINTER(ImageType)] + OPENJP2.opj_decode.restype = check_error + OPENJP2.opj_decode(codec, stream, image) @@ -761,6 +642,13 @@ def decode_tile_data(codec, tidx, data, data_size, stream): RuntimeError If the OpenJPEG library routine opj_decode fails. """ + OPENJP2.opj_decode_tile_data.argtypes = [CODEC_TYPE, + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_uint8), + ctypes.c_uint32, + STREAM_TYPE_P] + OPENJP2.opj_decode_tile_data.restype = check_error + datap = data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)) OPENJP2.opj_decode_tile_data(codec, ctypes.c_uint32(tidx), @@ -784,6 +672,9 @@ def create_decompress(codec_format): ------- codec : Reference to CODEC_TYPE instance. """ + OPENJP2.opj_create_decompress.argtypes = [CODEC_FORMAT_TYPE] + OPENJP2.opj_create_decompress.restype = CODEC_TYPE + codec = OPENJP2.opj_create_decompress(codec_format) return codec @@ -798,6 +689,7 @@ def destroy_codec(codec): codec : CODEC_TYPE Decompressor handle to destroy. """ + OPENJP2.opj_destroy_codec.argtypes = [CODEC_TYPE] OPENJP2.opj_destroy_codec(codec) @@ -818,6 +710,9 @@ def encode(codec, stream): RuntimeError If the OpenJPEG library routine opj_encode fails. """ + OPENJP2.opj_encode.argtypes = [CODEC_TYPE, STREAM_TYPE_P] + OPENJP2.opj_encode.restype = check_error + OPENJP2.opj_encode(codec, stream) @@ -836,6 +731,9 @@ def get_cstr_info(codec): cstr_info_p : CodestreamInfoV2 Reference to codestream information. """ + OPENJP2.opj_get_cstr_info.argtypes = [CODEC_TYPE] + OPENJP2.opj_get_cstr_info.restype = ctypes.POINTER(CodestreamInfoV2) + cstr_info_p = OPENJP2.opj_get_cstr_info(codec) return cstr_info_p @@ -861,6 +759,12 @@ def get_decoded_tile(codec, stream, imagep, tile_index): RuntimeError If the OpenJPEG library routine opj_get_decoded_tile fails. """ + OPENJP2.opj_get_decoded_tile.argtypes = [CODEC_TYPE, + STREAM_TYPE_P, + ctypes.POINTER(ImageType), + ctypes.c_uint32] + OPENJP2.opj_get_decoded_tile.restype = check_error + OPENJP2.opj_get_decoded_tile(codec, stream, imagep, tile_index) @@ -874,6 +778,10 @@ def destroy_cstr_info(cstr_info_p): cstr_info_p : CodestreamInfoV2 pointer Pointer to codestream info structure. """ + ARGTYPES = [ctypes.POINTER(ctypes.POINTER(CodestreamInfoV2))] + OPENJP2.opj_destroy_cstr_info.argtypes = ARGTYPES + OPENJP2.opj_destroy_cstr_info.restype = ctypes.c_void_p + OPENJP2.opj_destroy_cstr_info(ctypes.byref(cstr_info_p)) @@ -894,6 +802,8 @@ def end_compress(codec, stream): RuntimeError If the OpenJPEG library routine opj_end_compress fails. """ + OPENJP2.opj_end_compress.argtypes = [CODEC_TYPE, STREAM_TYPE_P] + OPENJP2.opj_end_compress.restype = check_error OPENJP2.opj_end_compress(codec, stream) @@ -914,6 +824,8 @@ def end_decompress(codec, stream): RuntimeError If the OpenJPEG library routine opj_end_decompress fails. """ + OPENJP2.opj_end_decompress.argtypes = [CODEC_TYPE, STREAM_TYPE_P] + OPENJP2.opj_end_decompress.restype = check_error OPENJP2.opj_end_decompress(codec, stream) @@ -927,6 +839,8 @@ def image_destroy(image): image : ImageType pointer Image resource to be disposed. """ + OPENJP2.opj_image_destroy.argtypes = [ctypes.POINTER(ImageType)] + OPENJP2.opj_image_destroy(image) @@ -947,6 +861,11 @@ def image_create(comptparms, clrspc): image : ImageType Reference to ImageType instance. """ + OPENJP2.opj_image_create.argtypes = [ctypes.c_uint32, + ctypes.POINTER(ImageComptParmType), + COLOR_SPACE_TYPE] + OPENJP2.opj_image_create.restype = ctypes.POINTER(ImageType) + image = OPENJP2.opj_image_create(len(comptparms), comptparms, clrspc) @@ -970,6 +889,12 @@ def image_tile_create(comptparms, clrspc): image : ImageType Reference to ImageType instance. """ + ARGTYPES = [ctypes.c_uint32, + ctypes.POINTER(ImageComptParmType), + COLOR_SPACE_TYPE] + OPENJP2.opj_image_tile_create.argtypes = ARGTYPES + OPENJP2.opj_image_tile_create.restype = ctypes.POINTER(ImageType) + image = OPENJP2.opj_image_tile_create(len(comptparms), comptparms, clrspc) @@ -998,6 +923,11 @@ def read_header(stream, codec): RuntimeError If the OpenJPEG library routine opj_read_header fails. """ + ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE, + ctypes.POINTER(ctypes.POINTER(ImageType))] + OPENJP2.opj_read_header.argtypes + OPENJP2.opj_read_header.restype = check_error + imagep = ctypes.POINTER(ImageType)() OPENJP2.opj_read_header(stream, codec, ctypes.byref(imagep)) return imagep @@ -1035,6 +965,19 @@ def read_tile_header(codec, stream): RuntimeError If the OpenJPEG library routine opj_read_tile_header fails. """ + ARGTYPES = [CODEC_TYPE, + STREAM_TYPE_P, + ctypes.POINTER(ctypes.c_uint32), + ctypes.POINTER(ctypes.c_uint32), + ctypes.POINTER(ctypes.c_int32), + ctypes.POINTER(ctypes.c_int32), + ctypes.POINTER(ctypes.c_int32), + ctypes.POINTER(ctypes.c_int32), + ctypes.POINTER(ctypes.c_uint32), + ctypes.POINTER(BOOL_TYPE)] + OPENJP2.opj_read_tile_header.argtypes = ARGTYPES + OPENJP2.opj_read_tile_header.restype = check_error + tile_index = ctypes.c_uint32() data_size = ctypes.c_uint32() col0 = ctypes.c_int32() @@ -1086,6 +1029,14 @@ def set_decode_area(codec, image, start_x=0, start_y=0, end_x=0, end_y=0): RuntimeError If the OpenJPEG library routine opj_set_decode_area fails. """ + OPENJP2.opj_set_decode_area.argtypes = [CODEC_TYPE, + ctypes.POINTER(ImageType), + ctypes.c_int32, + ctypes.c_int32, + ctypes.c_int32, + ctypes.c_int32] + OPENJP2.opj_set_decode_area.restype = check_error + OPENJP2.opj_set_decode_area(codec, image, ctypes.c_int32(start_x), ctypes.c_int32(start_y), @@ -1103,6 +1054,9 @@ def set_default_decoder_parameters(): dparam : DecompressionParametersType Decompression parameters. """ + ARGTYPES = [ctypes.POINTER(DecompressionParametersType)] + OPENJP2.opj_set_default_decoder_parameters.argtypes = ARGTYPES + dparams = DecompressionParametersType() OPENJP2.opj_set_default_decoder_parameters(ctypes.byref(dparams)) return dparams @@ -1138,6 +1092,9 @@ def set_default_encoder_parameters(): cparameters : CompressionParametersType Compression parameters. """ + ARGTYPES = [ctypes.POINTER(CompressionParametersType)] + OPENJP2.opj_set_default_encoder_parameters.argtypes = ARGTYPES + cparams = CompressionParametersType() OPENJP2.opj_set_default_encoder_parameters(ctypes.byref(cparams)) return cparams @@ -1162,6 +1119,10 @@ def set_error_handler(codec, handler, data=None): RuntimeError If the OpenJPEG library routine opj_set_error_handler fails. """ + OPENJP2.opj_set_error_handler.argtypes = [CODEC_TYPE, + ctypes.c_void_p, + ctypes.c_void_p] + OPENJP2.opj_set_error_handler.restype = check_error OPENJP2.opj_set_error_handler(codec, handler, data) @@ -1184,6 +1145,10 @@ def set_info_handler(codec, handler, data=None): RuntimeError If the OpenJPEG library routine opj_set_info_handler fails. """ + OPENJP2.opj_set_info_handler.argtypes = [CODEC_TYPE, + ctypes.c_void_p, + ctypes.c_void_p] + OPENJP2.opj_set_info_handler.restype = check_error OPENJP2.opj_set_info_handler(codec, handler, data) @@ -1206,6 +1171,11 @@ def set_warning_handler(codec, handler, data=None): RuntimeError If the OpenJPEG library routine opj_set_warning_handler fails. """ + OPENJP2.opj_set_warning_handler.argtypes = [CODEC_TYPE, + ctypes.c_void_p, + ctypes.c_void_p] + OPENJP2.opj_set_warning_handler.restype = check_error + OPENJP2.opj_set_warning_handler(codec, handler, data) @@ -1226,6 +1196,10 @@ def setup_decoder(codec, dparams): RuntimeError If the OpenJPEG library routine opj_setup_decoder fails. """ + ARGTYPES = [CODEC_TYPE, ctypes.POINTER(DecompressionParametersType)] + OPENJP2.opj_setup_decoder.argtypes = ARGTYPES + OPENJP2.opj_setup_decoder.restype = check_error + OPENJP2.opj_setup_decoder(codec, ctypes.byref(dparams)) @@ -1249,6 +1223,11 @@ def setup_encoder(codec, cparams, image): RuntimeError If the OpenJPEG library routine opj_setup_encoder fails. """ + ARGTYPES = [CODEC_TYPE, + ctypes.POINTER(CompressionParametersType), + ctypes.POINTER(ImageType)] + OPENJP2.opj_setup_encoder.argtypes = ARGTYPES + OPENJP2.opj_setup_encoder.restype = check_error OPENJP2.opj_setup_encoder(codec, ctypes.byref(cparams), image) @@ -1271,6 +1250,11 @@ def start_compress(codec, image, stream): RuntimeError If the OpenJPEG library routine opj_start_compress fails. """ + OPENJP2.opj_start_compress.argtypes = [CODEC_TYPE, + ctypes.POINTER(ImageType), + STREAM_TYPE_P] + OPENJP2.opj_start_compress.restype = check_error + OPENJP2.opj_start_compress(codec, image, stream) @@ -1292,6 +1276,9 @@ def stream_create_default_file_stream(fptr, isa_read_stream): stream : stream_t An OpenJPEG file stream. """ + 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 read_stream = 1 if isa_read_stream else 0 stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream) return stream @@ -1315,6 +1302,9 @@ def stream_create_default_file_stream_v3(fname, isa_read_stream): stream : stream_t An OpenJPEG file stream. """ + 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 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, @@ -1332,6 +1322,7 @@ def stream_destroy(stream): stream : STREAM_TYPE_P The file stream. """ + OPENJP2.opj_stream_destroy.argtypes = [STREAM_TYPE_P] OPENJP2.opj_stream_destroy(stream) @@ -1345,6 +1336,7 @@ def stream_destroy_v3(stream): stream : STREAM_TYPE_P The file stream. """ + OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P] OPENJP2.opj_stream_destroy_v3(stream) @@ -1371,6 +1363,13 @@ def write_tile(codec, tile_index, data, data_size, stream): RuntimeError If the OpenJPEG library routine opj_write_tile fails. """ + OPENJP2.opj_write_tile.argtypes = [CODEC_TYPE, + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_uint8), + ctypes.c_uint32, + STREAM_TYPE_P] + OPENJP2.opj_write_tile.restype = check_error + datap = data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)) OPENJP2.opj_write_tile(codec, ctypes.c_uint32(int(tile_index)), From 89c086370641cf08e1663b933f42cc3534847352 Mon Sep 17 00:00:00 2001 From: John Evans Date: Mon, 30 Sep 2013 14:55:20 -0400 Subject: [PATCH 11/22] openjp2's decode_tile_data handler was returning the codec. It should not. #125 --- glymur/lib/openjp2.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index 43e4e61..82b0cfe 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -655,7 +655,6 @@ def decode_tile_data(codec, tidx, data, data_size, stream): datap, ctypes.c_uint32(data_size), stream) - return codec def create_decompress(codec_format): @@ -690,6 +689,7 @@ def destroy_codec(codec): Decompressor handle to destroy. """ OPENJP2.opj_destroy_codec.argtypes = [CODEC_TYPE] + OPENJP2.opj_destroy_codec.restype = ctypes.c_void_p OPENJP2.opj_destroy_codec(codec) @@ -840,6 +840,7 @@ def image_destroy(image): Image resource to be disposed. """ OPENJP2.opj_image_destroy.argtypes = [ctypes.POINTER(ImageType)] + OPENJP2.opj_image_destroy.restype = ctypes.c_void_p OPENJP2.opj_image_destroy(image) @@ -1056,6 +1057,7 @@ def set_default_decoder_parameters(): """ ARGTYPES = [ctypes.POINTER(DecompressionParametersType)] OPENJP2.opj_set_default_decoder_parameters.argtypes = ARGTYPES + OPENJP2.opj_set_default_decoder_parameters.restype = ctypes.c_void_p dparams = DecompressionParametersType() OPENJP2.opj_set_default_decoder_parameters(ctypes.byref(dparams)) @@ -1094,6 +1096,7 @@ def set_default_encoder_parameters(): """ ARGTYPES = [ctypes.POINTER(CompressionParametersType)] OPENJP2.opj_set_default_encoder_parameters.argtypes = ARGTYPES + OPENJP2.opj_set_default_encoder_parameters.restype = ctypes.c_void_p cparams = CompressionParametersType() OPENJP2.opj_set_default_encoder_parameters(ctypes.byref(cparams)) @@ -1323,6 +1326,7 @@ def stream_destroy(stream): The file stream. """ OPENJP2.opj_stream_destroy.argtypes = [STREAM_TYPE_P] + OPENJP2.opj_stream_destroy.restype = ctypes.c_void_p OPENJP2.opj_stream_destroy(stream) @@ -1337,6 +1341,7 @@ def stream_destroy_v3(stream): The file stream. """ OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P] + OPENJP2.opj_stream_destroy_v3.restype = ctypes.c_void_p OPENJP2.opj_stream_destroy_v3(stream) From 9422bccffb6a1806652b9e05470c30c5cb4b5c16 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 1 Oct 2013 10:55:28 -0400 Subject: [PATCH 12/22] Forgot to actually set argtypes for openjp2 read_header function. #125 --- glymur/lib/openjp2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index 82b0cfe..f056db0 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -926,7 +926,7 @@ def read_header(stream, codec): """ ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE, ctypes.POINTER(ctypes.POINTER(ImageType))] - OPENJP2.opj_read_header.argtypes + OPENJP2.opj_read_header.argtypes = ARGTYPES OPENJP2.opj_read_header.restype = check_error imagep = ctypes.POINTER(ImageType)() From 1fc1a635e0aeb1c88d35a23c04c14d1f131b13c7 Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 1 Oct 2013 10:56:27 -0400 Subject: [PATCH 13/22] Fixed typo. --- glymur/test/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glymur/test/test_config.py b/glymur/test/test_config.py index 16af663..32f067a 100644 --- a/glymur/test/test_config.py +++ b/glymur/test/test_config.py @@ -124,7 +124,7 @@ class TestConfig(unittest.TestCase): @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_write_without_library(self): - """Don't have openjp2 library? Must error out. + """Don't have openjpeg libraries? Must error out. """ data = glymur.Jp2k(self.j2kfile).read() with patch('glymur.lib.openjp2.OPENJP2', new=None): From c65ae274a95320a44463f986ca05d375d8b03481 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 2 Oct 2013 15:32:38 -0400 Subject: [PATCH 14/22] Minor tweaks. --- docs/source/detailed_installation.rst | 9 +++++---- docs/source/how_do_i.rst | 16 ++++++++-------- docs/source/introduction.rst | 22 ++++++---------------- glymur/version.py | 1 + 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst index 3f42275..3e4d9dc 100644 --- a/docs/source/detailed_installation.rst +++ b/docs/source/detailed_installation.rst @@ -7,8 +7,8 @@ Most users won't need to read this! You've been warned... Glymur Configuration '''''''''''''''''''''' -The default glymur installation process relies upon OpenJPEG version -1.X being properly installed on your system. If you have version 1.5 you can +The default glymur installation process relies upon OpenJPEG +being properly installed on your system. If you have version 1.5 you can both read and write JPEG 2000 files, but you may wish to install version 2.0 or the 2.0+ version from OpenJPEG's development trunk for better performance. If you do that, you should compile it as a shared library (named *openjp2* @@ -35,7 +35,7 @@ i.e. :: This assumes, of course, that you've installed OpenJPEG into /opt/openjp2-svn on a linux system. The location of the configuration file -is platform-dependent (of course). If you use either linux or mac, the path +can vary as well (of course). If you use either linux or mac, the path to the configuration file would normally be :: $HOME/.config/glymur/glymurrc @@ -71,7 +71,8 @@ packages/RPMs/ports/whatever without going through pip. Mac OS X -------- All the necessary packages are available to use glymur with Python 2.6, 2.7, -and 3.3 via MacPorts. You should install the following set of ports: +and 3.3 via MacPorts. For python 3.3, you should install the following set of +ports: * python33 * py33-numpy diff --git a/docs/source/how_do_i.rst b/docs/source/how_do_i.rst index cc0b227..212862d 100644 --- a/docs/source/how_do_i.rst +++ b/docs/source/how_do_i.rst @@ -3,7 +3,7 @@ How do I...? ------------ -read the lowest resolution thumbnail? +... read the lowest resolution thumbnail? ===================================== Printing the Jp2k object should reveal the number of resolutions (look in the COD segment section), but you can take a shortcut by supplying -1 as the @@ -14,7 +14,7 @@ resolution level. :: >>> j = glymur.Jp2k(file) >>> thumbnail = j.read(rlevel=-1) -display metadata? +... display metadata? ================= There are two ways. From the unix command line, the script *jp2dump* is available. :: @@ -34,7 +34,7 @@ codestream box, only the main header is printed. It is possible to print >>> print(j.get_codestream()) -add XML metadata? +... add XML metadata? ================= You can append any number of XML boxes to a JP2 file (not to a raw codestream). Consider the following XML file `data.xml` : :: @@ -66,7 +66,7 @@ The **append** method can add an XML box as shown below:: >>> jp2.append(xmlbox) >>> print(jp2) -add metadata in a more general fashion? +... add metadata in a more general fashion? ======================================= An existing raw codestream (or JP2 file) can be wrapped (re-wrapped) in a user-defined set of JP2 boxes. To get just a minimal JP2 jacket on the @@ -157,18 +157,18 @@ to add metadata, you should keep in mind that **wrap** produces a new JP2 file, while **append** modifies an existing file and is currently limited to XML boxes. -create an image with an alpha layer? +... create an image with an alpha layer? ==================================== OpenJPEG can create JP2 files with more than 3 components (requires -the development version), but by default, any extra components are +the development version of OpenJPEG), 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 . -Instead of a circular mask, however, we'll make it an ellipse since the source -image isn't square. +Instead of a circular mask we'll make it an ellipse since the source +image isn't square. :: >>> import numpy as np >>> import glymur diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 37dcb5a..e1a3b3b 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -4,9 +4,8 @@ Glymur: a Python interface for JPEG 2000 **Glymur** is an interface to the OpenJPEG library which allows one to read and write JPEG 2000 files from within Python. -Glymur supports both reading and writing of JPEG 2000 images. Writing -JPEG 2000 images is currently limited to images that can fit in memory, -however. +Glymur supports both reading and writing of JPEG 2000 images, but writing +JPEG 2000 images is currently limited to images that can fit in memory Of particular focus is retrieval of metadata. Reading Exif UUIDs is supported, as is reading XMP UUIDs as the XMP data packet is just XML. There is @@ -24,11 +23,10 @@ only supported with the 1.5 or better, 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 windows installers provided to you by the OpenJPEG +If you use MacPorts or if you have a sufficiently recent version of +Linux, your package manager should already provide you with a version of +OpenJPEG 1.X which glymur can already use. If your platform is windows, +I suggest using the windows installers provided to you by the OpenJPEG folks at https://code.google.com/p/openjpeg/downloads/list . Glymur Installation @@ -47,13 +45,5 @@ line, so you should adjust your **$PATH** to take advantage of it. For example, if you install with pip's `--user` option on linux :: - $ export PYTHONPATH=$HOME/.local/lib/python3.3/site-packages $ export PATH=$HOME/.local/bin:$PATH -You can run the tests from within python as follows:: - - >>> import glymur - >>> glymur.runtests() - -Many tests are currently skipped, but the The important thing is whether or -not any tests fail. diff --git a/glymur/version.py b/glymur/version.py index 5e4122b..9603798 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -6,6 +6,7 @@ # # License: MIT +import pdb; pdb.set_trace() import sys import numpy as np from distutils.version import LooseVersion From 62db4f7e0fb2ef7c2b4a6141f5b5ec77e4ca5589 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 2 Oct 2013 17:18:12 -0400 Subject: [PATCH 15/22] Mistakenly committed with a pdb breakpoint set. --- glymur/version.py | 1 - 1 file changed, 1 deletion(-) diff --git a/glymur/version.py b/glymur/version.py index 9603798..5e4122b 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -6,7 +6,6 @@ # # License: MIT -import pdb; pdb.set_trace() import sys import numpy as np from distutils.version import LooseVersion From 7697230a591d3a3cf409e17ea83d2a03dd9c1057 Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 07:59:24 -0400 Subject: [PATCH 16/22] Change openjpeg version from base system version to tarball version 1.5. #126 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4898e65..beff6ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,10 @@ python: before_install: - sudo apt-get update -qq - - sudo apt-get install -qq libopenjpeg2 - sudo apt-get install -qq python-numpy - sudo apt-get install -qq python3-numpy + - wget http://openjpeg.googlecode.com/files/openjpeg-1.5.0-Linux-x86_64.tar.gz + - sudo tar -xvf openjpeg-1.5.0-Linux-x86_64.tar.gz --strip-components=1 -C / # command to install dependencies install: "pip install -r travis-requirements.txt --use-mirrors" From 9bff3731168de3d38eefa01dd42c4b20c5bbc75d Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 08:06:38 -0400 Subject: [PATCH 17/22] Use verbose to be sure of what tests are running. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index beff6ab..0eb0319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ before_install: install: "pip install -r travis-requirements.txt --use-mirrors" # command to run tests -script: "python -m unittest discover" +script: "python -m unittest discover -v" notifications: email: "john.g.evans.ne@gmail.com" From c5900755ca55e6584b424c16c8075916a6fdabe1 Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 10:02:16 -0400 Subject: [PATCH 18/22] Removing verbose option. Closes #126 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0eb0319..beff6ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ before_install: install: "pip install -r travis-requirements.txt --use-mirrors" # command to run tests -script: "python -m unittest discover -v" +script: "python -m unittest discover" notifications: email: "john.g.evans.ne@gmail.com" From 6edb59ac7e876f1e0e0050f581ddbf86f350fe2d Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 10:10:36 -0400 Subject: [PATCH 19/22] Very stupid, cannot import glymur in setup.py! #127 The install requirements have not yet necessarily been taken care of yet, so pip installs will break on 2.6 and 2.7, probably due to contextlib2. --- setup.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 8d62705..b5b62aa 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,8 @@ kwargs = {'name': 'Glymur', 'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k']}, 'scripts': ['bin/jp2dump'], 'license': 'MIT', - 'test_suite': 'glymur.test'} + 'test_suite': 'glymur.test', + 'version': '0.5.4'} instllrqrs = ['numpy>=1.4.1'] if sys.hexversion < 0x03030000: @@ -38,7 +39,4 @@ clssfrs = ["Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules"] kwargs['classifiers'] = clssfrs -import glymur -kwargs['version'] = glymur.version.version - setup(**kwargs) From fe3964c212bbc08aff42d42cc08c8b2a7a72262e Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 10:41:12 -0400 Subject: [PATCH 20/22] Add some information about how many tests should be expected to skip. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index beff6ab..cd80fa2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ before_install: install: "pip install -r travis-requirements.txt --use-mirrors" # command to run tests +# 2.7 should skip no more than 342 tests +# 3.3 should skip no more than 339 tests script: "python -m unittest discover" notifications: From 244fb26d9643144e772f950dd9d6e0373fa64d6e Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 10:41:41 -0400 Subject: [PATCH 21/22] Change the way versioning is included in setup.py #127 Using regular expressions should work so long as the pattern in glymur/version.py does not change. --- glymur/version.py | 2 ++ setup.py | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/glymur/version.py b/glymur/version.py index 5e4122b..04004fd 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -13,6 +13,8 @@ from distutils.version import LooseVersion from .lib import openjpeg as opj from .lib import openjp2 as opj2 +# Do not change the format of this next line! Doing so risks breaking +# setup.py version = "0.5.4" _sv = LooseVersion(version) version_tuple = _sv.version diff --git a/setup.py b/setup.py index b5b62aa..89e69f7 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,6 @@ from setuptools import setup, find_packages +import os +import re import sys kwargs = {'name': 'Glymur', @@ -12,8 +14,7 @@ kwargs = {'name': 'Glymur', 'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k']}, 'scripts': ['bin/jp2dump'], 'license': 'MIT', - 'test_suite': 'glymur.test', - 'version': '0.5.4'} + 'test_suite': 'glymur.test'} instllrqrs = ['numpy>=1.4.1'] if sys.hexversion < 0x03030000: @@ -39,4 +40,11 @@ clssfrs = ["Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules"] kwargs['classifiers'] = clssfrs +# Get the version string. Cannot do this by importing glymur! +version_file = os.path.join('glymur', 'version.py') +with open('glymur/version.py', 'rt') as fptr: + contents = fptr.read() + match = re.search('version\s*=\s*"(?P\d*.\d*.\d*.*)"\n', contents) + kwargs['version'] = match.group('version') + setup(**kwargs) From 3b6d34531dc4b3438b5581605105bb40d47e367d Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 3 Oct 2013 11:09:58 -0400 Subject: [PATCH 22/22] Finalizing for 0.5.5 release. --- CHANGES.txt | 4 +++- docs/source/conf.py | 2 +- glymur/version.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d0e2bce..30387a6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,9 +1,11 @@ +Oct 03, 2013 - v0.5.5 Fixed pip install error introduced in 0.5.0. + Sep 24, 2013 - v0.5.4 Fixed test error restricted to v2.0. Sep 24, 2013 - v0.5.3 Removed a duplicated channel definition test in test_jp2box that could cause a segfault in 1.3 if not properly skipped. -Sep 23, 2013 - v0.5.2 Fixed some teests that have been failing since 0.5. +Sep 23, 2013 - v0.5.2 Fixed some tests that have been failing since 0.5. under various edge cases. Sep 19, 2013 - v0.5.1 Added more resiliency to XML box parsing. Fixed tests diff --git a/docs/source/conf.py b/docs/source/conf.py index e01cc91..f847e68 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.5' # The full version, including alpha/beta/rc tags. -release = '0.5.4' +release = '0.5.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/glymur/version.py b/glymur/version.py index 04004fd..192859f 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -15,7 +15,7 @@ from .lib import openjp2 as opj2 # Do not change the format of this next line! Doing so risks breaking # setup.py -version = "0.5.4" +version = "0.5.5" _sv = LooseVersion(version) version_tuple = _sv.version