diff --git a/glymur/core.py b/glymur/core.py index 73a286e..6483558 100644 --- a/glymur/core.py +++ b/glymur/core.py @@ -74,6 +74,12 @@ _color_type_map_display = { _PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', _UNSPECIFIED: 'unspecified'} +# color channel definitions. +RED = 1 +GREEN = 2 +BLUE = 3 +GREY = 1 + # enumerated color channel associations _rgb_colorspace = {"R": 1, "G": 2, "B": 3} _greyscale_colorspace = {"Y": 1} diff --git a/glymur/jp2k.py b/glymur/jp2k.py index d755cb0..846d7fd 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -16,8 +16,7 @@ import warnings import numpy as np from .codestream import Codestream -from .core import progression_order -from .core import SRGB +from .core import * from .jp2box import * from .lib import openjp2 as opj2 @@ -467,6 +466,7 @@ class Jp2k(Jp2kBox): if len(colr_lst) == 0: msg = "The jp2 header box must contain a color definition box." raise IOError(msg) + colr = jp2h.box[colr_lst[0]] # Any cdef box must be in the jp2 header following the image header. cdef_lst = [j for (j, box) in enumerate(boxes) if box.id == 'cdef'] @@ -480,6 +480,21 @@ class Jp2k(Jp2kBox): msg = "Only one channel definition box is allowed in the " msg += "JP2 header." raise IOError(msg) + elif len(cdef_lst) == 1: + cdef = jp2h.box[cdef_lst[0]] + assn = cdef.association + typ = cdef.channel_type + index = cdef.index + if colr.colorspace == SRGB: + if any([chan + 1 not in assn or typ[chan] != 0 for chan in [0, 1, 2]]): + msg = "All color channels must be defined in the " + msg += "channel definition box." + raise IOError(msg) + elif colr.colorspace == GREYSCALE: + if 0 not in typ: + msg = "All color channels must be defined in the " + msg += "channel definition box." + raise IOError(msg) with open(filename, 'wb') as ofile: for box in boxes: diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 89b66d1..59a241c 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -111,6 +111,19 @@ class TestChannelDefinition(unittest.TestCase): self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0, 1)) self.assertEqual(jp2h.box[2].association, (1, 2, 3, 0)) + def test_bad_rgba(self): + """R, G, and B must be specified.""" + j2k = Jp2k(self.four_planes) + cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2, 3], + channel_type=[0, 0, 1, 1], + association=[1, 2, 3, 0]) + boxes = [self.ihdr, self.colr_rgb, cdef] + self.jp2h.box = boxes + boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] + with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: + with self.assertRaises(IOError) as ce: + j2k.wrap(tfile.name, boxes=boxes) + def test_grey(self): """Just regular greyscale.""" j2k = Jp2k(self.one_plane) @@ -151,6 +164,21 @@ class TestChannelDefinition(unittest.TestCase): self.assertEqual(jp2h.box[2].channel_type, (0, 1)) self.assertEqual(jp2h.box[2].association, (1, 0)) + def test_bad_grey_alpha(self): + """A greyscale image with alpha layer must specify Y""" + j2k = Jp2k(self.two_planes) + + # This cdef box + cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1], + channel_type=[1, 1], + association=[0, 1]) + boxes = [self.ihdr, self.colr_gr, cdef] + self.jp2h.box = boxes + boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] + with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: + with self.assertRaises((OSError, IOError)) as ce: + j2k.wrap(tfile.name, boxes=boxes) + def test_only_one_cdef_in_jp2_header(self): """There can only be one channel definition box in the jp2 header.""" j2k = Jp2k(self.j2kfile)