cdef box now only takes keyword arguments, index is optional.

Added negative test.  #64
This commit is contained in:
John Evans 2013-07-17 12:51:31 -04:00
commit 3c350361a0
3 changed files with 104 additions and 49 deletions

View file

@ -43,14 +43,14 @@ _COLORSPACE_MAP_DISPLAY = {
ROMM_RGB: 'ROMM-RGB'} ROMM_RGB: 'ROMM-RGB'}
# enumerated color channel types # enumerated color channel types
_COLOR = 0 COLOR = 0
_OPACITY = 1 OPACITY = 1
_PRE_MULTIPLIED_OPACITY = 2 PRE_MULTIPLIED_OPACITY = 2
_UNSPECIFIED = 65535 _UNSPECIFIED = 65535
_COLOR_TYPE_MAP_DISPLAY = { _COLOR_TYPE_MAP_DISPLAY = {
_COLOR: 'color', COLOR: 'color',
_OPACITY: 'opacity', OPACITY: 'opacity',
_PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
_UNSPECIFIED: 'unspecified'} _UNSPECIFIED: 'unspecified'}
# color channel definitions. # color channel definitions.
@ -58,6 +58,7 @@ RED = 1
GREEN = 2 GREEN = 2
BLUE = 3 BLUE = 3
GREY = 1 GREY = 1
WHOLE_IMAGE = 0
# enumerated color channel associations # enumerated color channel associations
_COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3}, _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3},

View file

@ -426,14 +426,23 @@ class ChannelDefinitionBox(Jp2kBox):
longname : str longname : str
more verbose description of the box. more verbose description of the box.
index : int index : int
number of the channel number of the channel. Defaults to monotonically increasing sequence,
i.e. [0, 1, 2, ...]
channel_type : int channel_type : int
type of the channel type of the channel
association : int association : int
index of the associated color index of the associated color
""" """
def __init__(self, index, channel_type, association, **kwargs): def __init__(self, index=None, channel_type=None, association=None, **kwargs):
Jp2kBox.__init__(self, box_id='cdef', longname='Channel Definition') Jp2kBox.__init__(self, box_id='cdef', longname='Channel Definition')
# channel type and association must be specified.
if channel_type is None or association is None:
raise IOError("channel_type and association must be specified.")
if index is None:
index = list(range(len(channel_type)))
if len(index) != len(channel_type) or len(index) != len(association): if len(index) != len(channel_type) or len(index) != len(association):
msg = "Length of channel definition box inputs must be the same." msg = "Length of channel definition box inputs must be the same."
raise IOError(msg) raise IOError(msg)
@ -504,8 +513,9 @@ class ChannelDefinitionBox(Jp2kBox):
channel_type = data[1:num_components * 6:3] channel_type = data[1:num_components * 6:3]
association = data[2:num_components * 6:3] association = data[2:num_components * 6:3]
box = ChannelDefinitionBox(index, channel_type, association, box = ChannelDefinitionBox(index=index, channel_type=channel_type,
length=length, offset=offset) association=association, length=length,
offset=offset)
return box return box

View file

@ -16,6 +16,8 @@ import pkg_resources
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k
from glymur.jp2box import * from glymur.jp2box import *
from glymur.core import COLOR, OPACITY
from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE
# Doc tests should be run as well. # Doc tests should be run as well.
@ -84,12 +86,19 @@ class TestChannelDefinition(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def test_rgb(self): def test_cdef_no_inputs(self):
"""channel_type and association are required inputs."""
with self.assertRaises(IOError):
glymur.jp2box.ChannelDefinitionBox()
def test_rgb_with_index(self):
"""Just regular RGB.""" """Just regular RGB."""
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
channel_type = [COLOR, COLOR, COLOR]
association = [RED, GREEN, BLUE]
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2],
channel_type=[0, 0, 0], channel_type=channel_type,
association=[1, 2, 3]) association=association)
boxes = [self.ihdr, self.colr_rgb, cdef] boxes = [self.ihdr, self.colr_rgb, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -101,15 +110,41 @@ class TestChannelDefinition(unittest.TestCase):
boxes = [box.box_id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1, 2)) self.assertEqual(jp2h.box[2].index, (0, 1, 2))
self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0)) self.assertEqual(jp2h.box[2].channel_type,
self.assertEqual(jp2h.box[2].association, (1, 2, 3)) (COLOR, COLOR, COLOR))
self.assertEqual(jp2h.box[2].association,
(RED, GREEN, BLUE))
def test_rgb(self):
"""Just regular RGB, but don't supply the optional index."""
j2k = Jp2k(self.j2kfile)
channel_type = [COLOR, COLOR, COLOR]
association = [RED, GREEN, BLUE]
cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
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:
j2k.wrap(tfile.name, boxes=boxes)
jp2 = Jp2k(tfile.name)
jp2h = jp2.box[2]
boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1, 2))
self.assertEqual(jp2h.box[2].channel_type,
(COLOR, COLOR, COLOR))
self.assertEqual(jp2h.box[2].association,
(RED, GREEN, BLUE))
def test_rgba(self): def test_rgba(self):
"""Just regular RGBA.""" """Just regular RGBA."""
j2k = Jp2k(self.four_planes) j2k = Jp2k(self.four_planes)
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2, 3], channel_type = (COLOR, COLOR, COLOR, OPACITY)
channel_type=[0, 0, 0, 1], association = (RED, GREEN, BLUE, WHOLE_IMAGE)
association=[1, 2, 3, 0]) cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
boxes = [self.ihdr, self.colr_rgb, cdef] boxes = [self.ihdr, self.colr_rgb, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -121,15 +156,16 @@ class TestChannelDefinition(unittest.TestCase):
boxes = [box.box_id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1, 2, 3)) self.assertEqual(jp2h.box[2].index, (0, 1, 2, 3))
self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0, 1)) self.assertEqual(jp2h.box[2].channel_type, channel_type)
self.assertEqual(jp2h.box[2].association, (1, 2, 3, 0)) self.assertEqual(jp2h.box[2].association, association)
def test_bad_rgba(self): def test_bad_rgba(self):
"""R, G, and B must be specified.""" """R, G, and B must be specified."""
j2k = Jp2k(self.four_planes) j2k = Jp2k(self.four_planes)
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2, 3], channel_type = (COLOR, COLOR, OPACITY, OPACITY)
channel_type=[0, 0, 1, 1], association = (RED, GREEN, BLUE, WHOLE_IMAGE)
association=[1, 2, 3, 0]) cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
boxes = [self.ihdr, self.colr_rgb, cdef] boxes = [self.ihdr, self.colr_rgb, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -140,9 +176,10 @@ class TestChannelDefinition(unittest.TestCase):
def test_grey(self): def test_grey(self):
"""Just regular greyscale.""" """Just regular greyscale."""
j2k = Jp2k(self.one_plane) j2k = Jp2k(self.one_plane)
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0], channel_type = (COLOR,)
channel_type=[0], association = (GREY,)
association=[1]) cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
boxes = [self.ihdr, self.colr_gr, cdef] boxes = [self.ihdr, self.colr_gr, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -154,15 +191,16 @@ class TestChannelDefinition(unittest.TestCase):
boxes = [box.box_id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0,)) self.assertEqual(jp2h.box[2].index, (0,))
self.assertEqual(jp2h.box[2].channel_type, (0,)) self.assertEqual(jp2h.box[2].channel_type, channel_type)
self.assertEqual(jp2h.box[2].association, (1,)) self.assertEqual(jp2h.box[2].association, association)
def test_grey_alpha(self): def test_grey_alpha(self):
"""Just regular greyscale plus alpha.""" """Just regular greyscale plus alpha."""
j2k = Jp2k(self.two_planes) j2k = Jp2k(self.two_planes)
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1], channel_type = (COLOR, OPACITY)
channel_type=[0, 1], association = (GREY, WHOLE_IMAGE)
association=[1, 0]) cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
boxes = [self.ihdr, self.colr_gr, cdef] boxes = [self.ihdr, self.colr_gr, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -174,17 +212,19 @@ class TestChannelDefinition(unittest.TestCase):
boxes = [box.box_id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1)) self.assertEqual(jp2h.box[2].index, (0, 1))
self.assertEqual(jp2h.box[2].channel_type, (0, 1)) self.assertEqual(jp2h.box[2].channel_type, channel_type)
self.assertEqual(jp2h.box[2].association, (1, 0)) self.assertEqual(jp2h.box[2].association, association)
def test_bad_grey_alpha(self): def test_bad_grey_alpha(self):
"""A greyscale image with alpha layer must specify Y""" """A greyscale image with alpha layer must specify a color channel"""
j2k = Jp2k(self.two_planes) j2k = Jp2k(self.two_planes)
channel_type = (OPACITY, OPACITY)
association = (GREY, WHOLE_IMAGE)
# This cdef box # This cdef box
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1], cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
channel_type=[1, 1], association=association)
association=[0, 1])
boxes = [self.ihdr, self.colr_gr, cdef] boxes = [self.ihdr, self.colr_gr, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -196,9 +236,10 @@ class TestChannelDefinition(unittest.TestCase):
"""There can only be one channel definition box in the jp2 header.""" """There can only be one channel definition box in the jp2 header."""
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], channel_type = (COLOR, COLOR, COLOR)
channel_type=[0, 0, 0], association = (RED, GREEN, BLUE)
association=[1, 2, 3]) cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
boxes = [self.ihdr, cdef, self.colr_rgb, cdef] boxes = [self.ihdr, cdef, self.colr_rgb, cdef]
self.jp2h.box = boxes self.jp2h.box = boxes
@ -214,9 +255,10 @@ class TestChannelDefinition(unittest.TestCase):
boxes = [self.ihdr, self.colr_rgb] boxes = [self.ihdr, self.colr_rgb]
self.jp2h.box = boxes self.jp2h.box = boxes
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], channel_type = (COLOR, COLOR, COLOR)
channel_type=[0, 0, 0], association = (RED, GREEN, BLUE)
association=[1, 2, 3]) cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
boxes = [self.jP, self.ftyp, self.jp2h, cdef, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, cdef, self.jp2c]
@ -228,18 +270,20 @@ class TestChannelDefinition(unittest.TestCase):
# Channel types are limited to 0, 1, 2, 65535 # Channel types are limited to 0, 1, 2, 65535
# Should reject if not all of index, channel_type, association the # Should reject if not all of index, channel_type, association the
# same length. # same length.
channel_type = (COLOR, COLOR, 3)
association = (RED, GREEN, BLUE)
with self.assertRaises(IOError): with self.assertRaises(IOError):
box = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], box = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
channel_type=[0, 0, 3], association=association)
association=[1, 2, 3])
def test_wrong_lengths(self): def test_wrong_lengths(self):
# Should reject if not all of index, channel_type, association the # Should reject if not all of index, channel_type, association the
# same length. # same length.
channel_type = (COLOR, COLOR)
association = (RED, GREEN, BLUE)
with self.assertRaises(IOError): with self.assertRaises(IOError):
box = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], box = glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
channel_type=[0, 0], association=association)
association=[1, 2, 3])
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")