More negative tests. Incompatible change to ChannelDefinitionBox. #175

This commit is contained in:
jevans 2014-02-22 21:30:52 -05:00
commit f8ced317db
5 changed files with 73 additions and 30 deletions

View file

@ -1,13 +1,14 @@
Feb 09, 2014 - Removed support for Python 2.6. Added write support for JP2
UUID, DataEntryURL, Palette and Component Mapping boxes, JPX
Association, NumberList and DataReference boxes. Added read
support for JPX free, number list, data reference, fragment
table, and fragment list boxes. Improved JPX Reader Requirements box
support. Added get_printoptions, set_printoptions functions.
Palette box now a 2D numpy array instead of a list of 1D arrays.
JP2 super box constructors now take optional box list argument.
Fixed bug where JPX files with more than one codestream but
advertising jp2 compatibility were not being read.
Feb 09, 2014 - Changed constructor for ChannelDefinition box. Removed support
for Python 2.6. Added write support for JP2 UUID, DataEntryURL,
Palette and Component Mapping boxes, JPX Association, NumberList
and DataReference boxes. Added read support for JPX free,
number list, data reference, fragment table, and fragment list
boxes. Improved JPX Reader Requirements box support. Added
get_printoptions, set_printoptions functions. Palette box now
a 2D numpy array instead of a list of 1D arrays. JP2 super box
constructors now take optional box list argument. Fixed bug
where JPX files with more than one codestream but advertising
jp2 compatibility were not being read.
Jan 28, 2014 - v0.5.10 Fixed bad warning when reader requirements box mask
length is unsupported.

View file

@ -11,6 +11,7 @@ ChangeLog
* added write support for JP2 UUID, dataEntryURL, palette, and component mapping boxes
* added read/write support for JPX free, number list, and data reference boxes
* Added read support for JPX fragment list and fragment table boxes
* incompatible change to channel definition box constructor, channel_type and association are no longer keyword arguments
* incompatible change to palette box constructor, it now takes a 2D numpy array instead of a list of 1D arrays
0.5.0 (September 16, 2013)

View file

@ -359,7 +359,7 @@ channel, but we aren't doing that). ::
>>> from glymur.core import RED, GREEN, BLUE, WHOLE_IMAGE
>>> asoc = [RED, GREEN, BLUE, WHOLE_IMAGE]
>>> cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=ctype, association=asoc)
>>> cdef = glymur.jp2box.ChannelDefinitionBox(ctype, asoc)
>>> print(cdef)
Channel Definition Box (cdef) @ (0, 0)
Channel 0 (color) ==> (1)

View file

@ -544,23 +544,28 @@ class ChannelDefinitionBox(Jp2kBox):
association : list
index of the associated color
"""
def __init__(self, index=None, channel_type=None, association=None,
**kwargs):
def __init__(self, channel_type, association, index=None, **kwargs):
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)))
self.index = tuple(range(len(channel_type)))
else:
self.index = tuple(index)
if len(index) != len(channel_type) or len(index) != len(association):
self.channel_type = tuple(channel_type)
self.association = tuple(association)
self.__dict__.update(**kwargs)
self._validate()
def _validate(self):
"""Verify that the box obeys the specifications."""
# channel type and association must be specified.
if not (len(self.index) == len(self.channel_type) == len(self.association)):
msg = "Length of channel definition box inputs must be the same."
raise IOError(msg)
# channel types must be one of 0, 1, 2, 65535
if any(x not in [0, 1, 2, 65535] for x in channel_type):
if any(x not in [0, 1, 2, 65535] for x in self.channel_type):
msg = "Channel types must be in the set of\n\n"
msg += " 0 - colour image data for associated color\n"
msg += " 1 - opacity\n"
@ -568,10 +573,6 @@ class ChannelDefinitionBox(Jp2kBox):
msg += " 65535 - unspecified"
raise IOError(msg)
self.index = tuple(index)
self.channel_type = tuple(channel_type)
self.association = tuple(association)
self.__dict__.update(**kwargs)
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -597,6 +598,7 @@ class ChannelDefinitionBox(Jp2kBox):
def write(self, fptr):
"""Write a channel definition box to file.
"""
self._validate()
num_components = len(self.association)
fptr.write(struct.pack('>I', 8 + 2 + num_components * 6))
fptr.write('cdef'.encode('utf-8'))
@ -634,9 +636,10 @@ class ChannelDefinitionBox(Jp2kBox):
channel_type = data[1:num_components * 6:3]
association = data[2:num_components * 6:3]
box = ChannelDefinitionBox(index=index, channel_type=channel_type,
association=association, length=length,
offset=offset)
box = ChannelDefinitionBox(index=tuple(index),
channel_type=tuple(channel_type),
association=tuple(association),
length=length, offset=offset)
return box
@ -795,7 +798,6 @@ class ComponentMappingBox(Jp2kBox):
if _printoptions['short'] == True:
return msg
for k in range(len(self.component_index)):
if self.mapping_type[k] == 1:
msg += '\n Component {0} ==> palette column {1}'
@ -1050,7 +1052,6 @@ class FileTypeBox(Jp2kBox):
self.brand = brand
self.minor_version = minor_version
if compatibility_list is None:
# see W0102, pylint
self.compatibility_list = ['jp2 ']
else:
self.compatibility_list = compatibility_list
@ -1718,6 +1719,15 @@ class PaletteBox(Jp2kBox):
self.signed = signed
self.length = length
self.offset = offset
self._validate()
def _validate(self):
"""Verify that the box obeys the specifications."""
if ((len(self.bits_per_component) != len(self.signed)) or
(len(self.signed) != self.palette.shape[1])):
msg = "The length of the 'bits_per_component' and the 'signed' "
msg += "members must equal the number of columns of the palette."
raise IOError(msg)
def __repr__(self):
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
@ -1737,6 +1747,7 @@ class PaletteBox(Jp2kBox):
def write(self, fptr):
"""Write a Palette box to file.
"""
self._validate()
bytes_per_row = sum(self.bits_per_component) / 8
bytes_per_palette = bytes_per_row * self.palette.shape[0]
box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette

View file

@ -139,7 +139,7 @@ class TestChannelDefinition(unittest.TestCase):
def test_cdef_no_inputs(self):
"""channel_type and association are required inputs."""
with self.assertRaises(IOError):
with self.assertRaises(TypeError):
glymur.jp2box.ChannelDefinitionBox()
def test_rgb_with_index(self):
@ -451,6 +451,36 @@ class TestColourSpecificationBox(unittest.TestCase):
colr.write(tfile)
@unittest.skipIf(os.name == "nt",
"Problems using NamedTemporaryFile on windows.")
class TestPaletteBox(unittest.TestCase):
"""Test suite for pclr box instantiation."""
def setUp(self):
pass
def tearDown(self):
pass
def test_mismatched_bitdepth_signed(self):
"""bitdepth and signed arguments must have equal length"""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
bps = (8, 8, 8)
signed = (False, False)
with self.assertRaises(IOError):
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
def test_mismatched_signed_palette(self):
"""bitdepth and signed arguments must have equal length"""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
bps = (8, 8, 8, 8)
signed = (False, False, False, False)
with self.assertRaises(IOError):
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
class TestAppend(unittest.TestCase):
"""Tests for append method."""