More negative tests. Incompatible change to ChannelDefinitionBox. #175
This commit is contained in:
parent
4caa13a955
commit
f8ced317db
5 changed files with 73 additions and 30 deletions
21
CHANGES.txt
21
CHANGES.txt
|
|
@ -1,13 +1,14 @@
|
||||||
Feb 09, 2014 - Removed support for Python 2.6. Added write support for JP2
|
Feb 09, 2014 - Changed constructor for ChannelDefinition box. Removed support
|
||||||
UUID, DataEntryURL, Palette and Component Mapping boxes, JPX
|
for Python 2.6. Added write support for JP2 UUID, DataEntryURL,
|
||||||
Association, NumberList and DataReference boxes. Added read
|
Palette and Component Mapping boxes, JPX Association, NumberList
|
||||||
support for JPX free, number list, data reference, fragment
|
and DataReference boxes. Added read support for JPX free,
|
||||||
table, and fragment list boxes. Improved JPX Reader Requirements box
|
number list, data reference, fragment table, and fragment list
|
||||||
support. Added get_printoptions, set_printoptions functions.
|
boxes. Improved JPX Reader Requirements box support. Added
|
||||||
Palette box now a 2D numpy array instead of a list of 1D arrays.
|
get_printoptions, set_printoptions functions. Palette box now
|
||||||
JP2 super box constructors now take optional box list argument.
|
a 2D numpy array instead of a list of 1D arrays. JP2 super box
|
||||||
Fixed bug where JPX files with more than one codestream but
|
constructors now take optional box list argument. Fixed bug
|
||||||
advertising jp2 compatibility were not being read.
|
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
|
Jan 28, 2014 - v0.5.10 Fixed bad warning when reader requirements box mask
|
||||||
length is unsupported.
|
length is unsupported.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ ChangeLog
|
||||||
* added write support for JP2 UUID, dataEntryURL, palette, and component mapping boxes
|
* 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/write support for JPX free, number list, and data reference boxes
|
||||||
* Added read support for JPX fragment list and fragment table 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
|
* 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)
|
0.5.0 (September 16, 2013)
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,7 @@ channel, but we aren't doing that). ::
|
||||||
|
|
||||||
>>> from glymur.core import RED, GREEN, BLUE, WHOLE_IMAGE
|
>>> from glymur.core import RED, GREEN, BLUE, WHOLE_IMAGE
|
||||||
>>> asoc = [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)
|
>>> print(cdef)
|
||||||
Channel Definition Box (cdef) @ (0, 0)
|
Channel Definition Box (cdef) @ (0, 0)
|
||||||
Channel 0 (color) ==> (1)
|
Channel 0 (color) ==> (1)
|
||||||
|
|
|
||||||
|
|
@ -544,23 +544,28 @@ class ChannelDefinitionBox(Jp2kBox):
|
||||||
association : list
|
association : list
|
||||||
index of the associated color
|
index of the associated color
|
||||||
"""
|
"""
|
||||||
def __init__(self, index=None, channel_type=None, association=None,
|
def __init__(self, channel_type, association, index=None, **kwargs):
|
||||||
**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:
|
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."
|
msg = "Length of channel definition box inputs must be the same."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
# channel types must be one of 0, 1, 2, 65535
|
# 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 = "Channel types must be in the set of\n\n"
|
||||||
msg += " 0 - colour image data for associated color\n"
|
msg += " 0 - colour image data for associated color\n"
|
||||||
msg += " 1 - opacity\n"
|
msg += " 1 - opacity\n"
|
||||||
|
|
@ -568,10 +573,6 @@ class ChannelDefinitionBox(Jp2kBox):
|
||||||
msg += " 65535 - unspecified"
|
msg += " 65535 - unspecified"
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
self.index = tuple(index)
|
|
||||||
self.channel_type = tuple(channel_type)
|
|
||||||
self.association = tuple(association)
|
|
||||||
self.__dict__.update(**kwargs)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
msg = Jp2kBox.__str__(self)
|
||||||
|
|
@ -597,6 +598,7 @@ class ChannelDefinitionBox(Jp2kBox):
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a channel definition box to file.
|
"""Write a channel definition box to file.
|
||||||
"""
|
"""
|
||||||
|
self._validate()
|
||||||
num_components = len(self.association)
|
num_components = len(self.association)
|
||||||
fptr.write(struct.pack('>I', 8 + 2 + num_components * 6))
|
fptr.write(struct.pack('>I', 8 + 2 + num_components * 6))
|
||||||
fptr.write('cdef'.encode('utf-8'))
|
fptr.write('cdef'.encode('utf-8'))
|
||||||
|
|
@ -634,9 +636,10 @@ 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=index, channel_type=channel_type,
|
box = ChannelDefinitionBox(index=tuple(index),
|
||||||
association=association, length=length,
|
channel_type=tuple(channel_type),
|
||||||
offset=offset)
|
association=tuple(association),
|
||||||
|
length=length, offset=offset)
|
||||||
return box
|
return box
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -795,7 +798,6 @@ class ComponentMappingBox(Jp2kBox):
|
||||||
if _printoptions['short'] == True:
|
if _printoptions['short'] == True:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
for k in range(len(self.component_index)):
|
for k in range(len(self.component_index)):
|
||||||
if self.mapping_type[k] == 1:
|
if self.mapping_type[k] == 1:
|
||||||
msg += '\n Component {0} ==> palette column {1}'
|
msg += '\n Component {0} ==> palette column {1}'
|
||||||
|
|
@ -1050,7 +1052,6 @@ class FileTypeBox(Jp2kBox):
|
||||||
self.brand = brand
|
self.brand = brand
|
||||||
self.minor_version = minor_version
|
self.minor_version = minor_version
|
||||||
if compatibility_list is None:
|
if compatibility_list is None:
|
||||||
# see W0102, pylint
|
|
||||||
self.compatibility_list = ['jp2 ']
|
self.compatibility_list = ['jp2 ']
|
||||||
else:
|
else:
|
||||||
self.compatibility_list = compatibility_list
|
self.compatibility_list = compatibility_list
|
||||||
|
|
@ -1718,6 +1719,15 @@ class PaletteBox(Jp2kBox):
|
||||||
self.signed = signed
|
self.signed = signed
|
||||||
self.length = length
|
self.length = length
|
||||||
self.offset = offset
|
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):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
|
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
|
||||||
|
|
@ -1737,6 +1747,7 @@ class PaletteBox(Jp2kBox):
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a Palette box to file.
|
"""Write a Palette box to file.
|
||||||
"""
|
"""
|
||||||
|
self._validate()
|
||||||
bytes_per_row = sum(self.bits_per_component) / 8
|
bytes_per_row = sum(self.bits_per_component) / 8
|
||||||
bytes_per_palette = bytes_per_row * self.palette.shape[0]
|
bytes_per_palette = bytes_per_row * self.palette.shape[0]
|
||||||
box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette
|
box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ class TestChannelDefinition(unittest.TestCase):
|
||||||
|
|
||||||
def test_cdef_no_inputs(self):
|
def test_cdef_no_inputs(self):
|
||||||
"""channel_type and association are required inputs."""
|
"""channel_type and association are required inputs."""
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(TypeError):
|
||||||
glymur.jp2box.ChannelDefinitionBox()
|
glymur.jp2box.ChannelDefinitionBox()
|
||||||
|
|
||||||
def test_rgb_with_index(self):
|
def test_rgb_with_index(self):
|
||||||
|
|
@ -451,6 +451,36 @@ class TestColourSpecificationBox(unittest.TestCase):
|
||||||
colr.write(tfile)
|
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):
|
class TestAppend(unittest.TestCase):
|
||||||
"""Tests for append method."""
|
"""Tests for append method."""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue