From 881132fbef3e6897f42eb2e33dd6bb46d4d1067c Mon Sep 17 00:00:00 2001 From: John Evans Date: Tue, 25 Jun 2013 07:04:24 -0400 Subject: [PATCH] Closes #57 --- glymur/jp2k.py | 28 +++++++++++++++++++++------- glymur/test/test_jp2box.py | 18 ++++++++++++++++++ glymur/test/test_jp2k.py | 23 +++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 015b63b..6f20ae9 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -142,7 +142,7 @@ class Jp2k(Jp2kBox): def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None, cbsize=None, psizes=None, grid_offset=None, sop=False, subsam=None, tilesize=None, prog=None, modesw=None, - colorspace=None, verbose=False): + colorspace=None, verbose=False, mct=None): """Write image data to a JP2/JPX/J2k file. Intended usage of the various parameters follows that of OpenJPEG's opj_compress utility. @@ -168,6 +168,9 @@ class Jp2k(Jp2kBox): If true, write SOP marker after each header packet. grid_offset : tuple, optional Offset (DY, DX) of the origin of the image in the reference grid. + mct : bool, optional + Specifies usage of the multi component transform. If not + specified, defaults to True if the colorspace is RGB. modesw : int, optional Mode switch. 1 = BYPASS(LAZY) @@ -337,6 +340,18 @@ class Jp2k(Jp2kBox): else: colorspace = _cspace_map[colorspace] + if mct is None: + if colorspace == opj2._CLRSPC_SRGB: + cparams.tcp_mct = 1 + else: + cparams.tcp_mct = 0 + else: + if mct and colorspace == opj2._CLRSPC_GRAY: + msg = "Cannot specify usage of the multi component transform " + msg += "if the colorspace is gray." + raise IOError(msg) + cparams.tcp_mct = 1 if mct else 0 + if img_array.dtype == np.uint8: comp_prec = 8 elif img_array.dtype == np.uint16: @@ -373,12 +388,6 @@ class Jp2k(Jp2kBox): src = layer.ctypes.data ctypes.memmove(dest, src, layer.nbytes) - # set multi-component transform? - if image.contents.numcomps == 3: - cparams.tcp_mct = 1 - else: - cparams.tcp_mct = 0 - codec = opj2._create_compress(codec_fmt) if verbose: @@ -435,6 +444,11 @@ class Jp2k(Jp2kBox): jp2h_lst = [idx for (idx, box) in enumerate(boxes) if box.id == 'jp2h'] jp2h_idx = jp2h_lst[0] jp2c_lst = [idx for (idx, box) in enumerate(boxes) if box.id == 'jp2c'] + if len(jp2c_lst) == 0: + msg = "A codestream box must be defined in the outermost " + msg += "list of boxes." + raise IOError(msg) + jp2c_idx = jp2c_lst[0] if jp2h_idx >= jp2c_idx: msg = "The codestream box must be preceeded by a jp2 header box." diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 2562339..bf34347 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -310,6 +310,24 @@ class TestJp2Boxes(unittest.TestCase): with self.assertRaises(IOError): j2k.wrap(tfile.name, boxes=boxes) + def test_missing_codestream(self): + j2k = Jp2k(self.raw_codestream) + c = j2k.get_codestream() + height = c.segment[1].Ysiz + width = c.segment[1].Xsiz + num_components = len(c.segment[1].XRsiz) + + jP = JPEG2000SignatureBox() + ftyp = FileTypeBox() + jp2h = JP2HeaderBox() + ihdr = ImageHeaderBox(height=height, width=width, + num_components=num_components) + jp2h.box = [ihdr] + boxes = [jP, ftyp, jp2h] + with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: + with self.assertRaises(IOError): + j2k.wrap(tfile.name, boxes=boxes) + def test_missing_colr_box(self): j2k = Jp2k(self.raw_codestream) c = j2k.get_codestream() diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 479c1d7..aab2bca 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -76,6 +76,8 @@ class TestJp2k(unittest.TestCase): def setUp(self): self.jp2file = pkg_resources.resource_filename(glymur.__name__, "data/nemo.jp2") + self.j2kfile = pkg_resources.resource_filename(glymur.__name__, + "data/goodstuff.j2k") def tearDown(self): pass @@ -152,6 +154,27 @@ class TestJp2k(unittest.TestCase): subsetdata = j.read(area=(0, 0, 512, 512)) np.testing.assert_array_equal(tiledata, subsetdata) + def test_write_srgb_without_mct(self): + j2k = Jp2k(self.j2kfile) + expdata = j2k.read() + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') + ofile.write(expdata, mct=False) + actdata = ofile.read() + np.testing.assert_array_equal(actdata, expdata) + + c = ofile.get_codestream() + self.assertEqual(c.segment[2].SPcod[3], 0) # no mct + + def test_write_grayscale_with_mct(self): + # MCT usage makes no sense for grayscale images. + j2k = Jp2k(self.j2kfile) + expdata = j2k.read() + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') + with self.assertRaises(IOError): + ofile.write(expdata[:,:,0], mct=True) + def test_write_cprl(self): # Issue 17 j = Jp2k(self.jp2file)