From 3adb09e6f4ff46a150c589e66361bdb012817a1b Mon Sep 17 00:00:00 2001 From: John Evans Date: Fri, 12 Sep 2014 10:05:40 -0400 Subject: [PATCH 1/5] refactoring slice protocol tests --- glymur/test/test_jp2k.py | 100 +++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 9058bbf..fcf5ea0 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -52,74 +52,92 @@ def load_tests(loader, tests, ignore): return tests -class TestJp2k(unittest.TestCase): - """These tests should be run by just about all configuration.""" - +class TestSliceProtocol(unittest.TestCase): + """ + Test slice protocol, i.e. when using [ ] to read image data. + """ def setUp(self): self.jp2file = glymur.data.nemo() - self.j2kfile = glymur.data.goodstuff() + self.j2k = Jp2k(glymur.data.goodstuff()) self.jpxfile = glymur.data.jpxfile() def tearDown(self): pass - def test_slice_protocol_negative(self): - """ - """ - j = Jp2k(self.j2kfile) - + def test_resolution_strides_cannot_differ(self): with self.assertRaises(IndexError): # Strides in x/y directions cannot differ. - d = j[::2, ::3] + self.j2k[::2, ::3] + def test_resolution_strides_must_be_powers_of_two(self): with self.assertRaises(IndexError): - # Strides in x/y direction must be powers of 2. - d = j[::3, ::3] + self.j2k[::3, ::3] - # start and stop are not supported when slicing on Jp2k object + def test_start_and_resolution_stride_not_allowed_at_same_time(self): with self.assertRaises(IndexError): - d = j[2::2, 2::2] - with self.assertRaises(IndexError): - d = j[:8:2, :8:2] - with self.assertRaises(IndexError): - d = j[2:8:2, 2:8:2] + self.j2k[2::2, 2::2] - def test_slice_protocol_3d(self): - """ - """ - j = Jp2k(self.j2kfile) - all = j.read() + def test_stop_and_resolution_stride_not_allowed_at_same_time(self): + with self.assertRaises(IndexError): + self.j2k[:8:2, :8:2] - d = j[:,:,0] + def test_integer_index_in_3d(self): + all = self.j2k.read() + + d = self.j2k[:,:,0] np.testing.assert_array_equal(all[:,:,0], d) - d = j[:,:,1] + d = self.j2k[:,:,1] np.testing.assert_array_equal(all[:,:,1], d) - d = j[:,:,2] + d = self.j2k[:,:,2] np.testing.assert_array_equal(all[:,:,2], d) - d = j[:,:,1:3] + def test_slice_in_third_dimension(self): + all = self.j2k.read() + + d = self.j2k[:,:,1:3] np.testing.assert_array_equal(all[:,:,1:3], d) - d = j[::2, ::2, 1:3] + def test_reduce_resolution_and_slice_in_third_dimension(self): + d = self.j2k[::2, ::2, 1:3] all = j.read(rlevel=1) np.testing.assert_array_equal(all[:,:,1:3], d) - def test_slice_protocol_2d(self): - """ + def test_full_resolution_upper_left_quarter(self): + all = self.jp2[:] - """ - j = Jp2k(self.j2kfile) + d = j[:728, :1296] + np.testing.assert_array_equal(all[:728, :1296], d) - d = j[:] + def test_full_resolution_lower_left_quarter(self): + all = self.jp2[:] + + d = j[728:, :1296] + np.testing.assert_array_equal(all[728:, :1296], d) + + def test_full_resolution_upper_right_quarter(self): + """ + Slice protocol should work when not reducing resolution. + """ + all = j[:] + + d = j[:728, 1296:] + np.testing.assert_array_equal(all[:728, 1296:], d) + + def test_full_resolution_lower_right_quarter(self): + all = j[:] + + d = j[728:, 1296:] + np.testing.assert_array_equal(all[728:, :1296:], d) + + def test_slice_protocol_2d_reduce_resolution(self): + d = self.j2k[:] self.assertEqual(d.shape, (800, 480, 3)) - # Stride of one. d = j[::1, ::1] self.assertEqual(d.shape, (800, 480, 3)) - # Stride of 2. d = j[::2, ::2] self.assertEqual(d.shape, (400, 240, 3)) @@ -135,6 +153,18 @@ class TestJp2k(unittest.TestCase): d = j[::32, ::32] self.assertEqual(d.shape, (25, 15, 3)) +class TestJp2k(unittest.TestCase): + """These tests should be run by just about all configuration.""" + + def setUp(self): + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() + self.jpxfile = glymur.data.jpxfile() + + def tearDown(self): + pass + + @unittest.skipIf(os.name == "nt", "Unexplained failure on windows") def test_irreversible(self): """Irreversible""" From 276a74ecaa6a0b89fbb3bd517a50099c14ed2397 Mon Sep 17 00:00:00 2001 From: John Evans Date: Fri, 12 Sep 2014 10:06:09 -0400 Subject: [PATCH 2/5] starting work to allow full resolution slicing --- glymur/jp2k.py | 65 ++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 8463eb5..b33cbdf 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -770,38 +770,47 @@ class Jp2k(Jp2kBox): else: raise IndexError("Illegal syntax.") - if isinstance(pargs[0], tuple): - ridx = pargs[0][0] - cidx = pargs[0][1] + if not isinstance(pargs[0], tuple): + msg = "Unexpected situation, slicing invoked, but not passed " + msg += "a slice or tuple." + raise RuntimeError(msg) - if ((ridx.start is not None) or - (ridx.stop is not None) or - (cidx.start is not None) or - (cidx.stop is not None)): - msg = "Only strides are supported when slicing a Jp2k object." - raise IndexError(msg) + # Assuming tuple from now on. + ridx = pargs[0][0] + cidx = pargs[0][1] - if ridx.step is None and cidx.step is None: - step = 1 - elif ridx.step != cidx.step: - msg = "Row and column strides must be the same." - raise IndexError(msg) - else: - step = ridx.step - - if np.log2(step) != np.floor(np.log2(step)): - msg = "Row and column strides must be powers of 2." - raise IndexError(msg) + if ((ridx.step is None) and (cidx.step is None)): + # Slicing with full resolution. + return self.read()[ridx, cidx] - data = self.read(rlevel=np.int(np.log2(step))) - if len(pargs[0]) == 2: - return data + if ((ridx.start is not None) or + (ridx.stop is not None) or + (cidx.start is not None) or + (cidx.stop is not None)): + msg = "Only strides are supported when slicing a Jp2k object." + raise IndexError(msg) - # Ok, 3 arguments in pargs. - if isinstance(pargs[0][2], slice): - return data[:,:,pargs[0][2]] - elif isinstance(pargs[0][2], int): - return data[:,:,pargs[0][2]] + if ridx.step is None and cidx.step is None: + step = 1 + elif ridx.step != cidx.step: + msg = "Row and column strides must be the same." + raise IndexError(msg) + else: + step = ridx.step + + if np.log2(step) != np.floor(np.log2(step)): + msg = "Row and column strides must be powers of 2." + raise IndexError(msg) + + data = self.read(rlevel=np.int(np.log2(step))) + if len(pargs[0]) == 2: + return data + + # Ok, 3 arguments in pargs. + if isinstance(pargs[0][2], slice): + return data[:,:,pargs[0][2]] + elif isinstance(pargs[0][2], int): + return data[:,:,pargs[0][2]] def read(self, **kwargs): From 6b66d4f6046681dde02f6ad7d9456311cc37c3b9 Mon Sep 17 00:00:00 2001 From: John Evans Date: Fri, 12 Sep 2014 10:06:59 -0400 Subject: [PATCH 3/5] Do not show VIM swp files in output of "git status" --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0d20b64..c9b568f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.swp From 79a0b7edb86e3e2b03a85d372f68d94cbf16a75c Mon Sep 17 00:00:00 2001 From: John Evans Date: Fri, 12 Sep 2014 11:08:22 -0400 Subject: [PATCH 4/5] Further along. --- glymur/jp2k.py | 4 +- glymur/test/test_jp2k.py | 92 ++++++++++++++++++++++------------------ 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index b33cbdf..c9021be 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -761,6 +761,7 @@ class Jp2k(Jp2kBox): def __getitem__(self, *pargs): """ + Slicing protocol. """ if isinstance(pargs[0], slice): # Should have a slice object where start = stop = step = None @@ -778,10 +779,11 @@ class Jp2k(Jp2kBox): # Assuming tuple from now on. ridx = pargs[0][0] cidx = pargs[0][1] + bidx = pargs[0][2] if ((ridx.step is None) and (cidx.step is None)): # Slicing with full resolution. - return self.read()[ridx, cidx] + return self.read()[ridx, cidx, bidx] if ((ridx.start is not None) or (ridx.stop is not None) or diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index fcf5ea0..bbbe657 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -56,13 +56,14 @@ class TestSliceProtocol(unittest.TestCase): """ Test slice protocol, i.e. when using [ ] to read image data. """ - def setUp(self): - self.jp2file = glymur.data.nemo() - self.j2k = Jp2k(glymur.data.goodstuff()) - self.jpxfile = glymur.data.jpxfile() + @classmethod + def setUpClass(self): - def tearDown(self): - pass + self.jp2 = Jp2k(glymur.data.nemo()) + self.jp2_data = self.jp2.read() + + self.j2k = Jp2k(glymur.data.goodstuff()) + self.j2k_data = self.j2k.read() def test_resolution_strides_cannot_differ(self): with self.assertRaises(IndexError): @@ -82,75 +83,84 @@ class TestSliceProtocol(unittest.TestCase): self.j2k[:8:2, :8:2] def test_integer_index_in_3d(self): - all = self.j2k.read() d = self.j2k[:,:,0] - np.testing.assert_array_equal(all[:,:,0], d) + np.testing.assert_array_equal(self.j2k_data[:,:,0], d) d = self.j2k[:,:,1] - np.testing.assert_array_equal(all[:,:,1], d) + np.testing.assert_array_equal(self.j2k_data[:,:,1], d) d = self.j2k[:,:,2] - np.testing.assert_array_equal(all[:,:,2], d) + np.testing.assert_array_equal(self.j2k_data[:,:,2], d) def test_slice_in_third_dimension(self): - all = self.j2k.read() - - d = self.j2k[:,:,1:3] - np.testing.assert_array_equal(all[:,:,1:3], d) + actual = self.j2k[:,:,1:3] + expected = self.j2k_data[:,:,1:3] + np.testing.assert_array_equal(actual, expected) def test_reduce_resolution_and_slice_in_third_dimension(self): d = self.j2k[::2, ::2, 1:3] - all = j.read(rlevel=1) + all = self.j2k.read(rlevel=1) np.testing.assert_array_equal(all[:,:,1:3], d) - def test_full_resolution_upper_left_quarter(self): - all = self.jp2[:] + def test_full_resolution_slicing_by_quarters(self): + # upper left + np.testing.assert_array_equal(self.jp2_data[:728, :1296], + self.jp2[:728, :1296]) + # lower left + np.testing.assert_array_equal(self.jp2_data[728:, :1296], + self.jp2[728:, :1296]) - d = j[:728, :1296] - np.testing.assert_array_equal(all[:728, :1296], d) + def test_full_resolution_slicing_by_quarters_upper_right(self): + actual = self.jp2[:728, 1296:] + expected = self.jp2_data[:728, 1296:] + np.testing.assert_array_equal(actual, expected) - def test_full_resolution_lower_left_quarter(self): - all = self.jp2[:] + def test_full_resolution_slicing_by_quarters_lower_right(self): + actual = self.jp2[728:, 1296:] + expected = self.jp2_data[728:, 1296:] + np.testing.assert_array_equal(actual, expected) - d = j[728:, :1296] - np.testing.assert_array_equal(all[728:, :1296], d) + def test_full_resolution_slicing_by_halves_left(self): + actual = self.jp2[:, :1296] + expected = self.jp2_data[:, :1296] + np.testing.assert_array_equal(actual, expected) - def test_full_resolution_upper_right_quarter(self): - """ - Slice protocol should work when not reducing resolution. - """ - all = j[:] + def test_full_resolution_slicing_by_right_half(self): + actual = self.jp2[:, 1296:] + expected = self.jp2_data[:, 1296:] + np.testing.assert_array_equal(actual, expected) - d = j[:728, 1296:] - np.testing.assert_array_equal(all[:728, 1296:], d) + def test_full_resolution_slicing_by_top_half(self): + actual = self.jp2[:728, :] + expected = self.jp2_data[:728, :] + np.testing.assert_array_equal(actual, expected) - def test_full_resolution_lower_right_quarter(self): - all = j[:] - - d = j[728:, 1296:] - np.testing.assert_array_equal(all[728:, :1296:], d) + def test_full_resolution_slicing_by_bottom_half(self): + actual = self.jp2[728:, :] + expected = self.jp2_data[728:, :] + np.testing.assert_array_equal(actual, expected) def test_slice_protocol_2d_reduce_resolution(self): d = self.j2k[:] self.assertEqual(d.shape, (800, 480, 3)) - d = j[::1, ::1] + d = self.j2k[::1, ::1] self.assertEqual(d.shape, (800, 480, 3)) - d = j[::2, ::2] + d = self.j2k[::2, ::2] self.assertEqual(d.shape, (400, 240, 3)) - d = j[::4, ::4] + d = self.j2k[::4, ::4] self.assertEqual(d.shape, (200, 120, 3)) - d = j[::8, ::8] + d = self.j2k[::8, ::8] self.assertEqual(d.shape, (100, 60, 3)) - d = j[::16, ::16] + d = self.j2k[::16, ::16] self.assertEqual(d.shape, (50, 30, 3)) - d = j[::32, ::32] + d = self.j2k[::32, ::32] self.assertEqual(d.shape, (25, 15, 3)) class TestJp2k(unittest.TestCase): From f7b7b9a0df1e589ace66c9a9b5d76680fdac3cb2 Mon Sep 17 00:00:00 2001 From: John Evans Date: Fri, 12 Sep 2014 16:46:08 -0400 Subject: [PATCH 5/5] __keyitem__ parameter now assumed to be either integer or slice. No longer erroring out when mixing resolution reduction with horiz/vert slicing, but neither is it tested. --- glymur/jp2k.py | 53 +++++++++++++++++----------------------- glymur/test/test_jp2k.py | 35 +++++++++----------------- 2 files changed, 34 insertions(+), 54 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index c9021be..6919555 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -759,60 +759,51 @@ class Jp2k(Jp2kBox): return boxes - def __getitem__(self, *pargs): + def __getitem__(self, pargs): """ Slicing protocol. """ - if isinstance(pargs[0], slice): + if isinstance(pargs, slice): + # Case of jp2[:] + # # Should have a slice object where start = stop = step = None - slc = pargs[0] + slc = pargs if slc.start is None and slc.stop is None and slc.step is None: return self.read() else: raise IndexError("Illegal syntax.") - if not isinstance(pargs[0], tuple): - msg = "Unexpected situation, slicing invoked, but not passed " - msg += "a slice or tuple." - raise RuntimeError(msg) + # Assuming pargs is a tuple from now on. + rows = pargs[0] + cols = pargs[1] + if len(pargs) == 2: + bands = slice(None, None, None) + else: + bands = pargs[2] - # Assuming tuple from now on. - ridx = pargs[0][0] - cidx = pargs[0][1] - bidx = pargs[0][2] - - if ((ridx.step is None) and (cidx.step is None)): + if ((rows.step is None) and (cols.step is None)): # Slicing with full resolution. - return self.read()[ridx, cidx, bidx] + # This can be improved to take advantage of tiling. + return self.read()[rows, cols, bands] - if ((ridx.start is not None) or - (ridx.stop is not None) or - (cidx.start is not None) or - (cidx.stop is not None)): - msg = "Only strides are supported when slicing a Jp2k object." - raise IndexError(msg) - - if ridx.step is None and cidx.step is None: - step = 1 - elif ridx.step != cidx.step: + if rows.step != cols.step: msg = "Row and column strides must be the same." raise IndexError(msg) - else: - step = ridx.step + + # Ok, reduce layer step is the same in both xy directions, so just take + # one of them. + step = rows.step if np.log2(step) != np.floor(np.log2(step)): msg = "Row and column strides must be powers of 2." raise IndexError(msg) data = self.read(rlevel=np.int(np.log2(step))) - if len(pargs[0]) == 2: + if len(pargs) == 2: return data # Ok, 3 arguments in pargs. - if isinstance(pargs[0][2], slice): - return data[:,:,pargs[0][2]] - elif isinstance(pargs[0][2], int): - return data[:,:,pargs[0][2]] + return data[:, :, bands] def read(self, **kwargs): diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index bbbe657..50b153a 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -74,24 +74,11 @@ class TestSliceProtocol(unittest.TestCase): with self.assertRaises(IndexError): self.j2k[::3, ::3] - def test_start_and_resolution_stride_not_allowed_at_same_time(self): - with self.assertRaises(IndexError): - self.j2k[2::2, 2::2] - - def test_stop_and_resolution_stride_not_allowed_at_same_time(self): - with self.assertRaises(IndexError): - self.j2k[:8:2, :8:2] - def test_integer_index_in_3d(self): - d = self.j2k[:,:,0] - np.testing.assert_array_equal(self.j2k_data[:,:,0], d) - - d = self.j2k[:,:,1] - np.testing.assert_array_equal(self.j2k_data[:,:,1], d) - - d = self.j2k[:,:,2] - np.testing.assert_array_equal(self.j2k_data[:,:,2], d) + for j in [0, 1, 2]: + band = self.j2k[:, :, j] + np.testing.assert_array_equal(self.j2k_data[:, :, j], band) def test_slice_in_third_dimension(self): actual = self.j2k[:,:,1:3] @@ -103,13 +90,15 @@ class TestSliceProtocol(unittest.TestCase): all = self.j2k.read(rlevel=1) np.testing.assert_array_equal(all[:,:,1:3], d) - def test_full_resolution_slicing_by_quarters(self): - # upper left - np.testing.assert_array_equal(self.jp2_data[:728, :1296], - self.jp2[:728, :1296]) - # lower left - np.testing.assert_array_equal(self.jp2_data[728:, :1296], - self.jp2[728:, :1296]) + def test_full_resolution_slicing_by_quarters_upper_left(self): + actual = self.jp2[:728, :1296] + expected = self.jp2_data[:728, :1296] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_quarters_lower_left(self): + actual = self.jp2[728:, :1296] + expected = self.jp2_data[728:, :1296] + np.testing.assert_array_equal(actual, expected) def test_full_resolution_slicing_by_quarters_upper_right(self): actual = self.jp2[:728, 1296:]