From 9be8bed66a459fe9ad5cff0e5f762db1b5af9a77 Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 24 Mar 2014 18:36:17 -0400 Subject: [PATCH 1/5] Updated for upstream openjpeg changes. #206 --- glymur/test/test_opj_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 40bfa68..efbc794 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -3447,7 +3447,7 @@ class TestSuiteDump(unittest.TestCase): # Image header self.assertEqual(jp2.box[2].box[0].height, 400) self.assertEqual(jp2.box[2].box[0].width, 700) - self.assertEqual(jp2.box[2].box[0].num_components, 1) + self.assertEqual(jp2.box[2].box[0].num_components, 3) self.assertEqual(jp2.box[2].box[0].bits_per_component, 8) self.assertEqual(jp2.box[2].box[0].signed, False) self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet From faf49b11c329dc56644ae38bc4450ed83fe99a7c Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 24 Mar 2014 20:54:49 -0400 Subject: [PATCH 2/5] Added ability to wrap more than one codestream. #206 --- glymur/jp2k.py | 43 +++++++++++++++++++++++----------- glymur/test/test_jp2box.py | 17 ++++++++++++++ glymur/test/test_jp2box_jpx.py | 12 ++++++++++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 1ae7839..fa2dfee 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -18,6 +18,7 @@ else: from collections import Counter import ctypes +import itertools import math import os import re @@ -606,7 +607,7 @@ class Jp2k(Jp2kBox): self.parse() def wrap(self, filename, boxes=None): - """Write the codestream back out to file, wrapped in new JP2 jacket. + """Create a new JP2/JPX file wrapped in a new jacket. Parameters ---------- @@ -640,10 +641,22 @@ class Jp2k(Jp2kBox): height = codestream.segment[1].ysiz width = codestream.segment[1].xsiz num_components = len(codestream.segment[1].xrsiz) + if num_components < 3: + colorspace = GREYSCALE + else: + if len(self.box) == 0: + # Best guess is SRGB + colorspace = SRGB + else: + # Take whatever the first jp2 header / color specification + # says. + jp2hs = [box for box in self.box if box.box_id == 'jp2h'] + colorspace = jp2hs[0].box[1].colorspace + boxes[2].box = [ImageHeaderBox(height=height, width=width, num_components=num_components), - ColourSpecificationBox(colorspace=SRGB)] + ColourSpecificationBox(colorspace=colorspace)] _validate_jp2_box_sequence(boxes) @@ -652,28 +665,30 @@ class Jp2k(Jp2kBox): if box.box_id != 'jp2c': box.write(ofile) else: - # The codestream gets written last. + # Codestreams require a bit more care. if len(self.box) == 0: # Am I a raw codestream? If so, then it is pretty # easy, just write the codestream box header plus all # of myself out to file. ofile.write(struct.pack('>I', self.length + 8)) - ofile.write('jp2c'.encode()) + ofile.write(b'jp2c') with open(self.filename, 'rb') as ifile: ofile.write(ifile.read()) else: # OK, I'm a jp2 file. Need to find out where the # raw codestream actually starts. - jp2c = [box for box in self.box - if box.box_id == 'jp2c'] - jp2c = jp2c[0] - ofile.write(struct.pack('>I', jp2c.length)) - ofile.write('jp2c'.encode()) + offset = box.offset + length = box.length + if offset == -1: + # Find the first codestream in the file. + jp2c = [box for box in self.box + if box.box_id == 'jp2c'] + offset = jp2c[0].offset + length = jp2c[0].length + with open(self.filename, 'rb') as ifile: - # Seek 8 bytes past the L, T fields to get to the - # raw codestream. - ifile.seek(jp2c.offset + 8) - ofile.write(ifile.read(jp2c.length - 8)) + ifile.seek(offset) + ofile.write(ifile.read(length)) ofile.flush() @@ -1301,7 +1316,7 @@ def _check_jp2h_child_boxes(boxes, parent_box_name): """Certain boxes can only reside in the JP2 header.""" box_ids = set([box.box_id for box in boxes]) intersection = box_ids.intersection(JP2H_CHILDREN) - if len(intersection) > 0 and parent_box_name != 'jp2h': + if len(intersection) > 0 and parent_box_name not in ['jp2h', 'jpch']: msg = "A '{0}' box can only be nested in a JP2 header box." raise IOError(msg.format(list(intersection)[0])) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index d537316..6f3bac9 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -58,6 +58,23 @@ class TestDataEntryURL(unittest.TestCase): def setUp(self): self.jp2file = glymur.data.nemo() + def test_wrap_greyscale(self): + """A single component should be wrapped as GREYSCALE.""" + j = Jp2k(self.jp2file) + data = j.read() + red = data[:, :, 0] + + # Write it back out as a raw codestream. + with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile1: + j2k = glymur.Jp2k(tfile1.name, 'wb') + j2k.write(data[:, :, 0]) + + # Ok, now rewrap it as JP2. The colorspace should be GREYSCALE. + with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2: + jp2 = j2k.wrap(tfile2.name) + self.assertEqual(jp2.box[2].box[1].colorspace, + glymur.core.GREYSCALE) + def test_basic_url(self): """Just your most basic URL box.""" # Wrap our j2k file in a JP2 box along with an interior url box. diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py index 98eac45..d9d697b 100644 --- a/glymur/test/test_jp2box_jpx.py +++ b/glymur/test/test_jp2box_jpx.py @@ -23,6 +23,7 @@ class TestJPXWrap(unittest.TestCase): """Test suite for wrapping JPX files.""" def setUp(self): + self.jpxfile = glymur.data.jpxfile() self.jp2file = glymur.data.nemo() self.j2kfile = glymur.data.goodstuff() @@ -44,6 +45,17 @@ class TestJPXWrap(unittest.TestCase): def tearDown(self): os.unlink(self.xmlfile) + def test_full_blown_jpx(self): + """Rewrap a jpx file.""" + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: + jpx = Jp2k(self.jpxfile) + idx = list(range(5)) + list(range(6, 9)) + list(range(9, 12)) + [12] + boxes = [jpx.box[j] for j in idx] + jpx2 = jpx.wrap(tfile1.name, boxes=boxes) + exp_ids = [box.box_id for box in boxes] + act_ids = [box.box_id for box in jpx2.box] + self.assertEqual(exp_ids, act_ids) + def test_jpx_ftbl_no_codestream(self): """Can have a jpx with no codestream.""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: From 9ecbc81e7aadb8444d1d72519b3a2dea9aafadfe Mon Sep 17 00:00:00 2001 From: jevans Date: Mon, 24 Mar 2014 21:26:04 -0400 Subject: [PATCH 3/5] Strengthened the test by reordering the boxes. #206 --- glymur/test/test_jp2box_jpx.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py index d9d697b..f7cfec2 100644 --- a/glymur/test/test_jp2box_jpx.py +++ b/glymur/test/test_jp2box_jpx.py @@ -49,12 +49,16 @@ class TestJPXWrap(unittest.TestCase): """Rewrap a jpx file.""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: jpx = Jp2k(self.jpxfile) - idx = list(range(5)) + list(range(6, 9)) + list(range(9, 12)) + [12] + idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12] boxes = [jpx.box[j] for j in idx] jpx2 = jpx.wrap(tfile1.name, boxes=boxes) exp_ids = [box.box_id for box in boxes] + lengths = [box.length for box in jpx.box] + exp_lengths = [lengths[j] for j in idx] act_ids = [box.box_id for box in jpx2.box] + act_lengths = [box.length for box in jpx2.box] self.assertEqual(exp_ids, act_ids) + self.assertEqual(exp_lengths, act_lengths) def test_jpx_ftbl_no_codestream(self): """Can have a jpx with no codestream.""" From 9b4e0a10fbef257399292ca469013bcc91d88540 Mon Sep 17 00:00:00 2001 From: jevans Date: Tue, 25 Mar 2014 21:17:29 -0400 Subject: [PATCH 4/5] More testing for jpx wrapping. Needs some refactoring, though. #206 --- glymur/jp2k.py | 39 ++++++++++++++++++++-- glymur/test/test_jp2box.py | 61 ++++++++++++++++++++++++++++++++++ glymur/test/test_jp2box_jpx.py | 15 --------- 3 files changed, 97 insertions(+), 18 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index fa2dfee..38e6973 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -607,7 +607,12 @@ class Jp2k(Jp2kBox): self.parse() def wrap(self, filename, boxes=None): - """Create a new JP2/JPX file wrapped in a new jacket. + """Create a new JP2/JPX file wrapped in a new set of JP2 boxes. + + This method is primarily aimed at wrapping a raw codestream in a set of + of JP2 boxes (turning it into a JP2 file instead of just a raw + codestream), or rewrapping a codestream in a JP2 file in a new "jacket" + of JP2 boxes. Parameters ---------- @@ -617,6 +622,8 @@ class Jp2k(Jp2kBox): JP2 box definitions to define the JP2 file format. If not provided, a default ""jacket" is assumed, consisting of JP2 signature, file type, JP2 header, and contiguous codestream boxes. + A JPX file rewrapped without the boxes argument results in a JP2 + file encompassing the first codestream. Returns ------- @@ -675,20 +682,46 @@ class Jp2k(Jp2kBox): with open(self.filename, 'rb') as ifile: ofile.write(ifile.read()) else: - # OK, I'm a jp2 file. Need to find out where the + # OK, I'm a jp2/jpx file. Need to find out where the # raw codestream actually starts. offset = box.offset length = box.length if offset == -1: + if self.box[1].brand == 'jpx ': + msg = "The codestream box must have its offset " + msg += "and length attributes fully specified " + msg += "if the file type brand is JPX." + raise IOError(msg) + # Find the first codestream in the file. jp2c = [box for box in self.box if box.box_id == 'jp2c'] offset = jp2c[0].offset length = jp2c[0].length + # Verify that the specified codestream is right. with open(self.filename, 'rb') as ifile: ifile.seek(offset) - ofile.write(ifile.read(length)) + read_buffer = ifile.read(8) + L, T = struct.unpack_from('>I4s', read_buffer, 0) + if T != b'jp2c': + msg = "Unable to locate the specified codestream." + raise IOError(msg) + if L == 0: + # The length of the box is presumed to last + # until the end of the file. Compute the + # effective length of the box. + L = os.path.getsize(ifile.name) - fptr.tell() + 8 + + elif L == 1: + # The length of the box is in the XL field, a + # 64-bit value. + read_buffer = ifile.read(8) + L, = struct.unpack('>Q', read_buffer) + + ifile.seek(offset) + read_buffer = ifile.read(L) + ofile.write(read_buffer) ofile.flush() diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 6f3bac9..f61bafc 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -858,6 +858,67 @@ class TestWrap(unittest.TestCase): with self.assertRaises(IOError): j2k.wrap(tfile.name, boxes=boxes) + def test_wrap_jpx_to_jp2_with_unadorned_jpch(self): + """A JPX file rewrapped with plain jpch is not allowed.""" + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: + jpx = Jp2k(self.jpxfile) + boxes = [jpx.box[0], jpx.box[1], jpx.box[2], + glymur.jp2box.ContiguousCodestreamBox()] + with self.assertRaises(IOError): + jpx.wrap(tfile1.name, boxes=boxes) + + def test_wrap_jpx_to_jp2_with_incorrect_jp2c_offset(self): + """Reject A JPX file rewrapped with bad jp2c offset.""" + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: + jpx = Jp2k(self.jpxfile) + jpch = jpx.box[5] + + # The offset should be 902. + jpch.offset = 901 + jpch.length = 313274 + boxes = [jpx.box[0], jpx.box[1], jpx.box[2], jpch] + with self.assertRaises(IOError): + jpx.wrap(tfile1.name, boxes=boxes) + + def test_wrap_jpx_to_jp2_with_correctly_specified_jp2c(self): + """Accept A JPX file rewrapped with good jp2c.""" + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: + jpx = Jp2k(self.jpxfile) + jpch = jpx.box[5] + + # This time get it right. + jpch.offset = 903 + jpch.length = 313274 + boxes = [jpx.box[0], jpx.box[1], jpx.box[2], jpch] + jp2 = jpx.wrap(tfile1.name, boxes=boxes) + + act_ids = [box.box_id for box in jp2.box] + exp_ids = ['jP ', 'ftyp', 'jp2h', 'jp2c'] + self.assertEqual(act_ids, exp_ids) + + act_offsets = [box.offset for box in jp2.box] + exp_offsets = [0, 12, 40, 887] + self.assertEqual(act_offsets, exp_offsets) + + act_lengths = [box.length for box in jp2.box] + exp_lengths = [12, 28, 847, 313274] + self.assertEqual(act_lengths, exp_lengths) + + def test_full_blown_jpx(self): + """Rewrap a jpx file.""" + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: + jpx = Jp2k(self.jpxfile) + idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12] + boxes = [jpx.box[j] for j in idx] + jpx2 = jpx.wrap(tfile1.name, boxes=boxes) + exp_ids = [box.box_id for box in boxes] + lengths = [box.length for box in jpx.box] + exp_lengths = [lengths[j] for j in idx] + act_ids = [box.box_id for box in jpx2.box] + act_lengths = [box.length for box in jpx2.box] + self.assertEqual(exp_ids, act_ids) + self.assertEqual(exp_lengths, act_lengths) + class TestJp2Boxes(unittest.TestCase): """Tests for canonical JP2 boxes.""" diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py index f7cfec2..73e8f1b 100644 --- a/glymur/test/test_jp2box_jpx.py +++ b/glymur/test/test_jp2box_jpx.py @@ -45,21 +45,6 @@ class TestJPXWrap(unittest.TestCase): def tearDown(self): os.unlink(self.xmlfile) - def test_full_blown_jpx(self): - """Rewrap a jpx file.""" - with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: - jpx = Jp2k(self.jpxfile) - idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12] - boxes = [jpx.box[j] for j in idx] - jpx2 = jpx.wrap(tfile1.name, boxes=boxes) - exp_ids = [box.box_id for box in boxes] - lengths = [box.length for box in jpx.box] - exp_lengths = [lengths[j] for j in idx] - act_ids = [box.box_id for box in jpx2.box] - act_lengths = [box.length for box in jpx2.box] - self.assertEqual(exp_ids, act_ids) - self.assertEqual(exp_lengths, act_lengths) - def test_jpx_ftbl_no_codestream(self): """Can have a jpx with no codestream.""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: From 6815a469022dd2cf9ff08bf671bafc528249666a Mon Sep 17 00:00:00 2001 From: jevans Date: Wed, 26 Mar 2014 21:04:28 -0400 Subject: [PATCH 5/5] Refactoring. #206 --- glymur/jp2k.py | 181 +++++++++++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 87 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 38e6973..1f37584 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -18,7 +18,6 @@ else: from collections import Counter import ctypes -import itertools import math import os import re @@ -30,7 +29,7 @@ import numpy as np from .codestream import Codestream from .core import SRGB, GREYSCALE -from .core import PROGRESSION_ORDER, RSIZ, CINEMA_MODE +from .core import PROGRESSION_ORDER, CINEMA_MODE from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE from .jp2box import Jp2kBox from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox @@ -443,7 +442,7 @@ class Jp2k(Jp2kBox): If glymur is unable to load the openjp2 library. """ if opj2.OPENJP2 is not None: - self._write_openjp2(img_array, verbose=verbose, **kwargs) + self._write_openjp2(img_array, verbose=verbose, **kwargs) elif opj.OPENJPEG is not None: self._write_openjpeg(img_array, verbose=verbose, **kwargs) else: @@ -639,31 +638,7 @@ class Jp2k(Jp2kBox): >>> jp2 = j2k.wrap(tfile.name) """ if boxes is None: - # Try to create a reasonable default. - boxes = [JPEG2000SignatureBox(), - FileTypeBox(), - JP2HeaderBox(), - ContiguousCodestreamBox()] - codestream = self.get_codestream() - height = codestream.segment[1].ysiz - width = codestream.segment[1].xsiz - num_components = len(codestream.segment[1].xrsiz) - if num_components < 3: - colorspace = GREYSCALE - else: - if len(self.box) == 0: - # Best guess is SRGB - colorspace = SRGB - else: - # Take whatever the first jp2 header / color specification - # says. - jp2hs = [box for box in self.box if box.box_id == 'jp2h'] - colorspace = jp2hs[0].box[1].colorspace - - boxes[2].box = [ImageHeaderBox(height=height, - width=width, - num_components=num_components), - ColourSpecificationBox(colorspace=colorspace)] + boxes = self._get_default_jp2_boxes() _validate_jp2_box_sequence(boxes) @@ -672,62 +647,92 @@ class Jp2k(Jp2kBox): if box.box_id != 'jp2c': box.write(ofile) else: - # Codestreams require a bit more care. - if len(self.box) == 0: - # Am I a raw codestream? If so, then it is pretty - # easy, just write the codestream box header plus all - # of myself out to file. - ofile.write(struct.pack('>I', self.length + 8)) - ofile.write(b'jp2c') - with open(self.filename, 'rb') as ifile: - ofile.write(ifile.read()) - else: - # OK, I'm a jp2/jpx file. Need to find out where the - # raw codestream actually starts. - offset = box.offset - length = box.length - if offset == -1: - if self.box[1].brand == 'jpx ': - msg = "The codestream box must have its offset " - msg += "and length attributes fully specified " - msg += "if the file type brand is JPX." - raise IOError(msg) - - # Find the first codestream in the file. - jp2c = [box for box in self.box - if box.box_id == 'jp2c'] - offset = jp2c[0].offset - length = jp2c[0].length - - # Verify that the specified codestream is right. - with open(self.filename, 'rb') as ifile: - ifile.seek(offset) - read_buffer = ifile.read(8) - L, T = struct.unpack_from('>I4s', read_buffer, 0) - if T != b'jp2c': - msg = "Unable to locate the specified codestream." - raise IOError(msg) - if L == 0: - # The length of the box is presumed to last - # until the end of the file. Compute the - # effective length of the box. - L = os.path.getsize(ifile.name) - fptr.tell() + 8 - - elif L == 1: - # The length of the box is in the XL field, a - # 64-bit value. - read_buffer = ifile.read(8) - L, = struct.unpack('>Q', read_buffer) - - ifile.seek(offset) - read_buffer = ifile.read(L) - ofile.write(read_buffer) - + self._write_wrapped_codestream(ofile, box) ofile.flush() jp2 = Jp2k(filename) return jp2 + def _write_wrapped_codestream(self, ofile, box): + """Write wrapped codestream.""" + # Codestreams require a bit more care. + # Am I a raw codestream? + if len(self.box) == 0: + # Yes, just write the codestream box header plus all + # of myself out to file. + ofile.write(struct.pack('>I', self.length + 8)) + ofile.write(b'jp2c') + with open(self.filename, 'rb') as ifile: + ofile.write(ifile.read()) + return + + # OK, I'm a jp2/jpx file. Need to find out where the raw codestream + # actually starts. + offset = box.offset + if offset == -1: + if self.box[1].brand == 'jpx ': + msg = "The codestream box must have its offset and " + msg += "length attributes fully specified if the file " + msg += "type brand is JPX." + raise IOError(msg) + + # Find the first codestream in the file. + jp2c = [box for box in self.box if box.box_id == 'jp2c'] + offset = jp2c[0].offset + + # Ready to write the codestream. + with open(self.filename, 'rb') as ifile: + ifile.seek(offset) + + # Verify that the specified codestream is right. + read_buffer = ifile.read(8) + L, T = struct.unpack_from('>I4s', read_buffer, 0) + if T != b'jp2c': + msg = "Unable to locate the specified codestream." + raise IOError(msg) + if L == 0: + # The length of the box is presumed to last until the end of + # the file. Compute the effective length of the box. + L = os.path.getsize(ifile.name) - ifile.tell() + 8 + + elif L == 1: + # The length of the box is in the XL field, a 64-bit value. + read_buffer = ifile.read(8) + L, = struct.unpack('>Q', read_buffer) + + ifile.seek(offset) + read_buffer = ifile.read(L) + ofile.write(read_buffer) + + def _get_default_jp2_boxes(self): + """Create a default set of JP2 boxes.""" + # Try to create a reasonable default. + boxes = [JPEG2000SignatureBox(), + FileTypeBox(), + JP2HeaderBox(), + ContiguousCodestreamBox()] + codestream = self.get_codestream() + height = codestream.segment[1].ysiz + width = codestream.segment[1].xsiz + num_components = len(codestream.segment[1].xrsiz) + if num_components < 3: + colorspace = GREYSCALE + else: + if len(self.box) == 0: + # Best guess is SRGB + colorspace = SRGB + else: + # Take whatever the first jp2 header / color specification + # says. + jp2hs = [box for box in self.box if box.box_id == 'jp2h'] + colorspace = jp2hs[0].box[1].colorspace + + boxes[2].box = [ImageHeaderBox(height=height, width=width, + num_components=num_components), + ColourSpecificationBox(colorspace=colorspace)] + + return boxes + def read(self, **kwargs): """Read a JPEG 2000 image. @@ -801,7 +806,8 @@ class Jp2k(Jp2kBox): msg += "the read_bands method instead." raise RuntimeError(msg) - def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False, verbose=False): + def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False, + verbose=False): """Read a JPEG 2000 image using libopenjpeg. Parameters @@ -836,9 +842,9 @@ class Jp2k(Jp2kBox): # -1 is shorthand for the largest rlevel rlevel = max_rlevel elif 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) + 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: @@ -970,7 +976,8 @@ class Jp2k(Jp2kBox): return img_array - def _populate_dparam(self, layer, rlevel, area, tile, ignore_pclr_cmap_cdef): + def _populate_dparam(self, layer, rlevel, area, tile, + ignore_pclr_cmap_cdef): """Populate decompression structure with appropriate input parameters. Parameters @@ -1244,11 +1251,11 @@ def _validate_jp2_box_sequence(boxes): _validate_jpx_box_sequence(boxes) else: count = _collect_box_count(boxes) - for id in count.keys(): - if id not in JP2_IDS: + for box_id in count.keys(): + if box_id not in JP2_IDS: msg = "The presence of a '{0}' box requires that the file type " msg += "brand be set to 'jpx '." - raise IOError(msg.format(id)) + raise IOError(msg.format(box_id)) def _validate_jpx_box_sequence(boxes): """Run through series of tests for JPX box legality."""