diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 5d39ed5..8f97e10 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -696,8 +696,9 @@ class Jp2k(Jp2kBox): (first_row, first_col, last_row, last_col) tile : int, optional Number of tile to decode. - no_cxform : bool - Whether or not to apply intended color transforms. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. verbose : bool, optional Print informational messages produced by the OpenJPEG library. @@ -752,7 +753,7 @@ class Jp2k(Jp2kBox): msg += "the read_bands method instead." raise RuntimeError(msg) - def _read_openjpeg(self, rlevel=0, no_cxform=False, verbose=False): + def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False, verbose=False): """Read a JPEG 2000 image using libopenjpeg. Parameters @@ -760,8 +761,9 @@ class Jp2k(Jp2kBox): rlevel : int, optional Factor by which to rlevel output resolution. Use -1 to get the lowest resolution thumbnail. - no_cxform : bool - Whether or not to apply intended color transforms. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. verbose : bool, optional Print informational messages produced by the OpenJPEG library. @@ -797,7 +799,7 @@ class Jp2k(Jp2kBox): dparameters = opj.DecompressionParametersType() opj.set_default_decoder_parameters(ctypes.byref(dparameters)) - if no_cxform is True: + if ignore_pclr_cmap_cdef is True: # Return raw codestream components. dparameters.flags |= 1 @@ -845,7 +847,7 @@ class Jp2k(Jp2kBox): return data def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None, - verbose=False, no_cxform=False): + verbose=False, ignore_pclr_cmap_cdef=False): """Read a JPEG 2000 image using libopenjp2. Parameters @@ -875,7 +877,8 @@ class Jp2k(Jp2kBox): """ self._subsampling_sanity_check() - dparam = self._populate_dparam(layer, rlevel, area, tile, no_cxform) + dparam = self._populate_dparam(layer, rlevel, area, tile, + ignore_pclr_cmap_cdef) with ExitStack() as stack: if hasattr(opj2.OPENJP2, @@ -919,7 +922,7 @@ class Jp2k(Jp2kBox): return img_array - def _populate_dparam(self, layer, rlevel, area, tile, no_cxform): + def _populate_dparam(self, layer, rlevel, area, tile, ignore_pclr_cmap_cdef): """Populate decompression structure with appropriate input parameters. Parameters @@ -933,8 +936,9 @@ class Jp2k(Jp2kBox): (first_row, first_col, last_row, last_col) tile : int Number of tile to decode. - no_cxform : bool - Whether or not to apply intended color transforms. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. Returns ------- @@ -972,14 +976,14 @@ class Jp2k(Jp2kBox): dparam.tile_index = tile dparam.nb_tile_to_decode = 1 - if no_cxform is True: + if ignore_pclr_cmap_cdef is True: # Return raw codestream components. dparam.flags |= 1 return dparam def read_bands(self, rlevel=0, layer=0, area=None, tile=None, - verbose=False, no_cxform=False): + verbose=False, ignore_pclr_cmap_cdef=False): """Read a JPEG 2000 image. The only time you should use this method is when the image has @@ -997,8 +1001,9 @@ class Jp2k(Jp2kBox): (first_row, first_col, last_row, last_col) tile : int, optional Number of tile to decode. - no_cxform : bool - Whether or not to apply intended color transforms. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. verbose : bool, optional Print informational messages produced by the OpenJPEG library. @@ -1028,7 +1033,7 @@ class Jp2k(Jp2kBox): "of OpenJP2 installed before using " "this functionality.") - dparam = self._populate_dparam(layer, rlevel, area, tile, no_cxform) + dparam = self._populate_dparam(layer, rlevel, area, tile, ignore_pclr_cmap_cdef) with ExitStack() as stack: if hasattr(opj2.OPENJP2, diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index f6eb691..3afe0a4 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -96,7 +96,7 @@ class TestJp2k(unittest.TestCase): """Indices for pclr jpxfile if no color transform""" j = Jp2k(self.jpxfile) rgb = j.read() - idx = j.read(no_cxform=True) + idx = j.read(ignore_pclr_cmap_cdef=True) self.assertEqual(rgb.shape, (1024, 1024, 3)) self.assertEqual(idx.shape, (1024, 1024)) @@ -109,6 +109,21 @@ class TestJp2k(unittest.TestCase): rgb_from_idx[r, c] = palette[idx[r, c]] np.testing.assert_array_equal(rgb, rgb_from_idx) + def test_no_cxform_cmap(self): + """Bands as physically ordered, not as physically intended""" + # This file has the components physically reversed. The cmap box + # tells the decoder how to order them, but this flag prevents that. + filename = opj_data_file('input/conformance/file2.jp2') + j = Jp2k(filename) + ycbcr = j.read() + crcby = j.read(ignore_pclr_cmap_cdef=True) + + expected = np.zeros(ycbcr.shape, ycbcr.dtype) + for k in range(crcby.shape[2]): + expected[:,:,crcby.shape[2] - k - 1] = crcby[:,:,k] + + np.testing.assert_array_equal(ycbcr, expected) + def test_file_not_present(self): """Should error out if reading from a file that does not exist""" # Verify that we error out appropriately if not given an existing file @@ -763,7 +778,7 @@ class TestJp2kOpjDataRoot(unittest.TestCase): filename = opj_data_file('input/conformance/file9.jp2') j = Jp2k(filename) rgb = j.read() - idx = j.read(no_cxform=True) + idx = j.read(ignore_pclr_cmap_cdef=True) self.assertEqual(rgb.shape, (512, 768, 3)) self.assertEqual(idx.shape, (512, 768)) @@ -805,7 +820,7 @@ class TestJp2kOpjDataRoot(unittest.TestCase): filename = opj_data_file('input/conformance/file2.jp2') j = Jp2k(filename) ycbcr = j.read() - crcby = j.read(no_cxform=True) + crcby = j.read(ignore_pclr_cmap_cdef=True) expected = np.zeros(ycbcr.shape, ycbcr.dtype) for k in range(crcby.shape[2]):