diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 7a96571..c3eae6c 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -9,6 +9,7 @@ ChangeLog * Added lxml requirement. * added set_printoptions, get_printoptions function * dropped support for Python 2.6, added support for Python 3.4 + * dropped support for OpenJPEG versions 1.3 and 1.4 * dropped windows support (it might work, it might not, I don't much care) * added write support for JP2 UUID, dataEntryURL, palette, and component mapping boxes * added read/write support for JPX free, number list, and data reference boxes diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 38193eb..10df117 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -14,17 +14,7 @@ XMP UUIDs. There is some very limited support for reading JPX metadata. Glymur 0.6 works on Python versions 2.7, 3.3 and 3.4. If you have Python 2.6, you should use the 0.5 series of Glymur. -OpenJPEG Installation -===================== -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 1.5 or better, however, and the trunk/development -version of OpenJPEG is strongly recommended. For more information about -OpenJPEG, please consult http://www.openjpeg.org. - -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. +For more information about OpenJPEG, please consult http://www.openjpeg.org. Glymur Installation =================== diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 7522d35..3467426 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -772,19 +772,6 @@ class Jp2k(Jp2kBox): """ self._subsampling_sanity_check() - if rlevel != 0: - # Must check the specified rlevel against the maximum. - # OpenJPEG 1.3 will segfault if rlevel is too high. - codestream = self.get_codestream() - max_rlevel = codestream.segment[2].spcod[4] - if rlevel == -1: - # -1 is shorthand for the largest rlevel - rlevel = max_rlevel - if rlevel < -1 or rlevel > max_rlevel: - msg = "rlevel must be in the range [-1, {0}] for this image." - msg = msg.format(max_rlevel) - raise IOError(msg) - with ExitStack() as stack: try: # Set decoding parameters. diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 7e4d9e8..2004d2b 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -554,15 +554,21 @@ UUID Box (uuid) @ (77, 3146) UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" -# Output of reader requirement printing for file7.jp2 -file7_rreq = r"""Reader Requirements Box (rreq) @ (44, 24) - Fully Understands Aspect Mask: 0xa0 - Display Completely Mask: 0xc0 +# Output of reader requirements printing for text_GBR.jp2 +text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109) + Fully Understands Aspect Mask: 0xffff + Display Completely Mask: 0xf8f0 Standard Features and Masks: - Feature 005: 0x80 Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 | ISO/IEC 15444-1 - Feature 060: 0x60 e-sRGB enumerated colorspace - Feature 043: 0x40 Deprecated - compositing layer uses restricted ICC profile - Vendor Features:""" + Feature 001: 0x8000 Deprecated - contains no extensions + Feature 005: 0x4080 Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 | ISO/IEC 15444-1 + Feature 012: 0x2040 Deprecated - codestream is contiguous + Feature 018: 0x1020 Deprecated - support for compositing is not required + Feature 044: 0x810 Compositing layer uses Any ICC profile + Vendor Features: + UUID 3a0d0218-0ae9-4115-b376-4bca41ce0e71 + UUID 47c92ccc-d1a1-4581-b904-38bb5467713b + UUID bc45a774-dd50-4ec6-a9f6-f3a137f47e90 + UUID d7c8c5ef-951f-43b2-8757-042500f538e8""" file1_xml = """XML Box (xml ) @ (36, 439) diff --git a/glymur/test/test_conformance.py b/glymur/test/test_conformance.py deleted file mode 100644 index 765f2f6..0000000 --- a/glymur/test/test_conformance.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -These tests deal with JPX/JP2/J2K images in the format-corpus repository. -""" -# R0904: Not too many methods in unittest. -# pylint: disable=R0904 - -# E1101: assertWarns introduced in python 3.2 -# pylint: disable=E1101 - -import os -from os.path import join -import re -import sys -import tempfile -import unittest - -import glymur -from glymur import Jp2k - -try: - FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT'] -except KeyError: - FORMAT_CORPUS_DATA_ROOT = None - -try: - OPJ_DATA_ROOT = os.environ['OPJ_DATA_ROOT'] -except KeyError: - OPJ_DATA_ROOT = None - - -@unittest.skipIf(sys.hexversion < 0x03020000, - "Requires features introduced in 3.2 (assertWarns)") -class TestSuiteConformance(unittest.TestCase): - """Test suite for conformance.""" - - def setUp(self): - self.j2kfile = glymur.data.goodstuff() - - def tearDown(self): - pass - - @unittest.skipIf(re.match(r"""1\.[0123]""", - glymur.version.openjpeg_version) is not None, - "Needs 1.3+ to catch this.") - def test_truncated_eoc(self): - """Has one byte shaved off of EOC marker.""" - with open(self.j2kfile, 'rb') as ifile: - data = ifile.read() - with tempfile.NamedTemporaryFile(suffix='.j2k') as ofile: - ofile.write(data[:-1]) - ofile.flush() - - j2k = Jp2k(ofile.name) - with self.assertWarns(UserWarning): - codestream = j2k.get_codestream(header_only=False) - - # The last segment is truncated, so there should not be an EOC - # marker. - self.assertNotEqual(codestream.segment[-1].marker_id, 'EOC') - - # The codestream is not as long as claimed. - with self.assertRaises(OSError): - j2k.read(rlevel=-1) - - -@unittest.skipIf(FORMAT_CORPUS_DATA_ROOT is None, - "FORMAT_CORPUS_DATA_ROOT environment variable not set") -@unittest.skipIf(sys.hexversion < 0x03020000, - "Requires features introduced in 3.2 (assertWarns)") -class TestSuiteFormatCorpus(unittest.TestCase): - """Test suite for files in format corpus repository.""" - - @unittest.skipIf(re.match(r"""1\.[01234]""", - glymur.version.openjpeg_version) is not None, - "Needs 1.4+ to catch this.") - def test_balloon_trunc2(self): - """Shortened by 5000 bytes.""" - jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT, - 'jp2k-test/byteCorruption/balloon_trunc2.jp2') - j2k = Jp2k(jfile) - with self.assertWarns(UserWarning): - codestream = j2k.get_codestream(header_only=False) - - # The last segment is truncated, so there should not be an EOC marker. - self.assertNotEqual(codestream.segment[-1].marker_id, 'EOC') - - # The codestream is not as long as claimed. - with self.assertRaises(OSError): - j2k.read(rlevel=-1) - - def test_balloon_trunc3(self): - """Most of last tile is missing.""" - jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT, - 'jp2k-test/byteCorruption/balloon_trunc3.jp2') - j2k = Jp2k(jfile) - with self.assertWarns(UserWarning): - codestream = j2k.get_codestream(header_only=False) - - # The last segment is truncated, so there should not be an EOC marker. - self.assertNotEqual(codestream.segment[-1].marker_id, 'EOC') - - # Should error out, it does not. - #with self.assertRaises(OSError): - # j2k.read(rlevel=-1) - - def test_jp2_brand_any_icc_profile(self): - """If 'jp2 ', then the method cannot be any icc profile.""" - jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT, - 'jp2k-test', 'icc', - 'balloon_eciRGBv2_ps_adobeplugin.jpf') - with self.assertWarns(UserWarning): - Jp2k(jfile) - - def test_jp2_brand_iccpr_mult_colr(self): - """Has colr box, one that conforms, one that does not.""" - - # Wrong 'brand' field; contains two versions of ICC profile: one - # embedded using "Any ICC" method; other embedded using "Restricted - # ICC" method, with description ("Modified eciRGB v2") and profileClass - # ("Input Device") changed relative to original profile. - jfile = join(FORMAT_CORPUS_DATA_ROOT, 'jp2k-test', 'icc', - 'balloon_eciRGBv2_ps_adobeplugin_jp2compatible.jpf') - with self.assertWarns(UserWarning): - Jp2k(jfile) - - -@unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_DATA_ROOT environment variable not set") -@unittest.skipIf(sys.hexversion < 0x03020000, - "Requires features introduced in 3.2 (assertWarns)") -class TestSuiteOpj(unittest.TestCase): - """Test suite for files in openjpeg repository.""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def test_jp2_brand_any_icc_profile(self): - """If 'jp2 ', then the method cannot be any icc profile.""" - filename = os.path.join(OPJ_DATA_ROOT, - 'input/nonregression/text_GBR.jp2') - with self.assertWarns(UserWarning): - Jp2k(filename) - -if __name__ == "__main__": - unittest.main() diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index dd55299..433531f 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -386,15 +386,9 @@ class TestJp2k(unittest.TestCase): # The file in question has multiple codestreams. jpx = Jp2k(self.jpxfile) data = jpx.read() - if re.match(r"""1\.[0123]""", glymur.version.openjpeg_version): - # openjpeg 1.3 doesn't apply the palette, so it's a 2D image here - self.assertEqual(data.shape, (1024, 1024)) - else: - self.assertEqual(data.shape, (1024, 1024, 3)) + self.assertEqual(data.shape, (1024, 1024, 3)) -@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version), - "Requires at least version 1.5") class TestJp2k_write(unittest.TestCase): """Write tests, can be run by versions 1.5+""" diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 426ae02..5cc9250 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -381,11 +381,7 @@ class TestSuite(unittest.TestCase): jfile = opj_data_file('input/conformance/file9.jp2') jp2k = Jp2k(jfile) jpdata = jp2k.read() - if re.match(r"""1\.3""", glymur.version.openjpeg_version): - # Version 1.3 reads the indexed image as indices, not as RGB. - self.assertEqual(jpdata.shape, (512, 768)) - else: - self.assertEqual(jpdata.shape, (512, 768, 3)) + self.assertEqual(jpdata.shape, (512, 768, 3)) def test_NR_DEC_Bretagne2_j2k_1_decode(self): jfile = opj_data_file('input/nonregression/Bretagne2.j2k') @@ -3337,7 +3333,6 @@ class TestSuiteDump(unittest.TestCase): self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 546) self.assertIsNone(jp2.box[2].box[1].colorspace) - def test_NR_file6_dump(self): jfile = opj_data_file('input/conformance/file6.jp2') jp2 = Jp2k(jfile) @@ -3398,11 +3393,6 @@ class TestSuiteDump(unittest.TestCase): # File type box. self.assertEqual(jp2.box[1].brand, 'jp2 ') self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ') - self.assertEqual(jp2.box[1].minor_version, 0) - - # Reader requirements talk. - # e-SRGB enumerated colourspace - #self.assertTrue(60 in jp2.box[2].standard_flag) # Jp2 Header # Image header @@ -3420,11 +3410,10 @@ class TestSuiteDump(unittest.TestCase): self.assertEqual(jp2.box[2].box[1].method, glymur.core.RESTRICTED_ICC_PROFILE) self.assertEqual(jp2.box[2].box[1].precedence, 0) - self.assertEqual(jp2.box[2].box[1].approximation, 1) # JPX exact + self.assertEqual(jp2.box[2].box[1].approximation, 1) self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 13332) self.assertIsNone(jp2.box[2].box[1].colorspace) - def test_NR_file8_dump(self): # One 8-bit component in a gamma 1.8 space. The colourspace is # specified using a Restricted ICC profile. @@ -6314,11 +6303,16 @@ class TestSuiteDump(unittest.TestCase): [8, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10]) def test_NR_text_GBR_dump(self): + # brand is 'jp2 ', but has any icc profile. + # Verify the warning on python3, but ignore it otherwise. jfile = opj_data_file('input/nonregression/text_GBR.jp2') - with warnings.catch_warnings(): - # brand is 'jp2 ', but has any icc profile. - warnings.simplefilter("ignore") - jp2 = Jp2k(jfile) + if sys.hexversion > 0x03030000: + with self.assertWarns(UserWarning): + jp2 = Jp2k(jfile) + else: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + jp2 = Jp2k(jfile) ids = [box.box_id for box in jp2.box] lst = ['jP ', 'ftyp', 'rreq', 'jp2h', diff --git a/glymur/test/test_opj_suite_neg.py b/glymur/test/test_opj_suite_neg.py index d31ea48..c93d624 100644 --- a/glymur/test/test_opj_suite_neg.py +++ b/glymur/test/test_opj_suite_neg.py @@ -23,8 +23,6 @@ from glymur import Jp2k import glymur -@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 TestSuiteNegative2pointzero(unittest.TestCase): @@ -48,8 +46,6 @@ class TestSuiteNegative2pointzero(unittest.TestCase): j.write(data, psnr=[30, 35, 40], cratios=[2, 3, 4]) -@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_opj_suite_write.py b/glymur/test/test_opj_suite_write.py index 220115c..1db11c4 100644 --- a/glymur/test/test_opj_suite_write.py +++ b/glymur/test/test_opj_suite_write.py @@ -249,9 +249,6 @@ class TestSuiteNegative2pointzero(unittest.TestCase): @unittest.skipIf(os.name == "nt", "no write support on windows, period") -@unittest.skipIf(re.match(r"""1\.[01234]\.\d""", - glymur.version.openjpeg_version) is not None, - "Writing only supported with openjpeg version 1.5+.") @unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index bc12216..0deaebd 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -740,14 +740,12 @@ class TestPrinting(unittest.TestCase): "OPJ_DATA_ROOT environment variable not set") def test_rreq(self): """verify printing of reader requirements box""" - filename = opj_data_file('input/conformance/file7.jp2') + filename = opj_data_file('input/nonregression/text_GBR.jp2') j = glymur.Jp2k(filename) with patch('sys.stdout', new=StringIO()) as fake_out: print(j.box[2]) actual = fake_out.getvalue().strip() - self.maxDiff = None - expected = fixtures.file7_rreq - self.assertEqual(actual, expected) + self.assertEqual(actual, fixtures.text_GBR_rreq) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set")