Merge branch 'devel' into issue104

This commit is contained in:
John Evans 2013-10-27 14:07:03 -04:00
commit 6948f40f27
8 changed files with 568 additions and 146 deletions

View file

@ -1,3 +1,6 @@
Oct 22, 2013 - v0.5.7 Super box constructors now take optional box list
argument. Removed ssiz attribute from SIZsegment class.
Oct 13, 2013 - v0.5.6 Fixed handling of non-ascii chars in XML boxes. Fixed
some docstring errors in jp2box module.

View file

@ -646,16 +646,50 @@ class Codestream(object):
length, = struct.unpack('>H', read_buffer)
xy_buffer = fptr.read(36)
data = struct.unpack('>HIIIIIIIIH', xy_buffer)
num_components, = struct.unpack('>H', xy_buffer[-2:])
rsiz = data[0]
xysiz = (data[1], data[2])
xyosiz = (data[3], data[4])
xytsiz = (data[5], data[6])
xytosiz = (data[7], data[8])
component_buffer = fptr.read(num_components * 3)
# Csiz is the number of components
Csiz = data[9]
segment = SIZsegment(xy_buffer, component_buffer, length, offset)
component_buffer = fptr.read(Csiz * 3)
data = struct.unpack('>' + 'B' * len(component_buffer),
component_buffer)
bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3])
signed = tuple(((x & 0xb0) > 0) for x in data[0::3])
xrsiz = data[1::3]
yrsiz = data[2::3]
for j, subsampling in enumerate(zip(xrsiz, yrsiz)):
if 0 in subsampling:
msg = "Invalid subsampling value for component {0}: "
msg += "dx={1}, dy={2}."
msg = msg.format(j, subsampling[0], subsampling[1])
warnings.warn(msg)
kwargs = {'rsiz': rsiz,
'xysiz': xysiz,
'xyosiz': xyosiz,
'xytsiz': xytsiz,
'xytosiz': xytosiz,
'Csiz': Csiz,
'bitdepth': bitdepth,
'signed': signed,
'xyrsiz': (xrsiz, yrsiz),
'length': length,
'offset': offset}
segment = SIZsegment(**kwargs)
# Need to keep track of the number of components from SIZ for
# other markers
self._csiz = len(segment.ssiz)
# other markers.
self._csiz = Csiz
return segment
@ -1441,8 +1475,8 @@ class SIZsegment(Segment):
Width and height of reference tile with respect to the reference grid.
xtosiz, ytosiz : int
Horizontal and vertical offsets of tile from origin of reference grid.
ssiz : iterable bytes
Encoded precision (depth) in bits and sign of each component.
Csiz : int
Number of components in image.
bitdepth : iterable bytes
Precision (depth) in bits of each component.
signed : iterable bool
@ -1457,21 +1491,20 @@ class SIZsegment(Segment):
15444-1:2004 - Information technology -- JPEG 2000 image coding system:
Core coding system
"""
def __init__(self, xy_buffer, component_buffer, length, offset):
Segment.__init__(self, marker_id='SIZ')
def __init__(self, rsiz=-1, xysiz=None, xyosiz=-1, xytsiz=-1, xytosiz=-1,
Csiz=-1, bitdepth=None, signed=None, xyrsiz=-1, length=-1,
offset=-1):
Segment.__init__(self, marker_id='SIZ', length=length, offset=offset)
data = struct.unpack('>HIIIIIIIIH', xy_buffer)
self.rsiz = data[0]
self.xsiz = data[1]
self.ysiz = data[2]
self.xosiz = data[3]
self.yosiz = data[4]
self.xtsiz = data[5]
self.ytsiz = data[6]
self.xtosiz = data[7]
self.ytosiz = data[8]
# disregarding the last element in data
self.rsiz = rsiz
self.xsiz, self.ysiz = xysiz
self.xosiz, self.yosiz = xyosiz
self.xtsiz, self.ytsiz = xytsiz
self.xtosiz, self.ytosiz = xytosiz
self.Csiz = Csiz
self.bitdepth = bitdepth
self.signed = signed
self.xrsiz, self.yrsiz = xyrsiz
num_tiles_x = (self.xsiz - self.xosiz) / (self.xtsiz - self.xtosiz)
num_tiles_y = (self.ysiz - self.yosiz) / (self.ytsiz - self.ytosiz)
@ -1480,26 +1513,23 @@ class SIZsegment(Segment):
msg = "Invalid number of tiles ({0}).".format(numtiles)
warnings.warn(msg)
data = struct.unpack('>' + 'B' * len(component_buffer),
component_buffer)
self.ssiz = data[0::3]
for j, subsampling in enumerate(list(zip(data[1::3], data[2::3]))):
if 0 in subsampling:
msg = "Invalid subsampling value for component {0}: "
msg += "dx={1}, dy={2}."
msg = msg.format(j, subsampling[0], subsampling[1])
warnings.warn(msg)
self.xrsiz = data[1::3]
self.yrsiz = data[2::3]
self.bitdepth = tuple(((x & 0x7f) + 1) for x in self.ssiz)
self.signed = tuple(((x & 0xb0) > 0) for x in self.ssiz)
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, "
msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, "
msg += "Csiz={Csiz}, bitdepth={bitdepth}, signed={signed}, "
msg += "xyrsiz={xyrsiz})"
msg = msg.format(rsiz=self.rsiz,
xysiz=(self.xsiz, self.ysiz),
xyosiz=(self.xosiz, self.yosiz),
xytsiz=(self.xtsiz, self.ytsiz),
xytosiz=(self.xtosiz, self.ytosiz),
Csiz=self.Csiz,
bitdepth=self.bitdepth,
signed=self.signed,
xyrsiz=(self.xrsiz, self.yrsiz))
return msg
def __str__(self):
msg = Segment.__str__(self)
msg += '\n '
@ -1549,6 +1579,9 @@ class SOCsegment(Segment):
Segment.__init__(self, marker_id='SOC')
self.__dict__.update(**kwargs)
def __repr__(self):
msg = "glymur.codestream.SOCsegment()"
return msg
class SODsegment(Segment):
"""Container for Start of Data (SOD) segment information.

View file

@ -75,6 +75,12 @@ class Jp2kBox(object):
self.offset = offset
self.longname = longname
def __repr__(self):
msg = "glymur.jp2box.Jp2kBox(box_id='{0}', offset={1}, length={2}, "
msg += "longname='{3}')"
msg = msg.format(self.box_id, self.offset, self.length, self.longname)
return msg
def __str__(self):
msg = "{0} Box ({1})".format(self.longname, self.box_id)
msg += " @ ({0}, {1})".format(self.offset, self.length)
@ -206,6 +212,17 @@ class ColourSpecificationBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.ColourSpecificationBox("
msg += "method={0}, precedence={1}, approximation={2}, colorspace={3}, "
msg += "icc_profile={4})"
msg = msg.format(self.method,
self.precedence,
self.approximation,
self.colorspace,
self.icc_profile)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -426,12 +443,12 @@ class ChannelDefinitionBox(Jp2kBox):
offset of the box from the start of the file.
longname : str
more verbose description of the box.
index : int
index : list
number of the channel. Defaults to monotonically increasing sequence,
i.e. [0, 1, 2, ...]
channel_type : int
channel_type : list
type of the channel
association : int
association : list
index of the associated color
"""
def __init__(self, index=None, channel_type=None, association=None,
@ -458,9 +475,9 @@ class ChannelDefinitionBox(Jp2kBox):
msg += " 65535 - unspecified"
raise IOError(msg)
self.index = index
self.channel_type = channel_type
self.association = association
self.index = tuple(index)
self.channel_type = tuple(channel_type)
self.association = tuple(association)
self.__dict__.update(**kwargs)
def __str__(self):
@ -475,6 +492,12 @@ class ChannelDefinitionBox(Jp2kBox):
msg = msg.format(self.index[j], color_type_string, assn)
return msg
def __repr__(self):
msg = "glymur.jp2box.ChannelDefinitionBox("
msg += "index={0}, channel_type={1}, association={2})"
msg = msg.format(self.index, self.channel_type, self.association)
return msg
def write(self, fptr):
"""Write a channel definition box to file.
"""
@ -537,11 +560,15 @@ class CodestreamHeaderBox(Jp2kBox):
box : list
List of boxes contained in this superbox.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=[], length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jpch', longname='Codestream Header')
self.length = length
self.offset = offset
self.box = []
self.box = box
def __repr__(self):
msg = "glymur.jp2box.CodestreamHeaderBox(box={0})".format(self.box)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -595,13 +622,18 @@ class CompositingLayerHeaderBox(Jp2kBox):
box : list
List of boxes contained in this superbox.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=[], length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jplh',
longname='Compositing Layer Header')
self.length = length
self.offset = offset
self.box = []
def __repr__(self):
msg = "glymur.jp2box.CompositingLayerHeaderBox(box={0})"
msg = msg.format(self.box)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
for box in self.box:
@ -650,11 +682,11 @@ class ComponentMappingBox(Jp2kBox):
Offset of the box from the start of the file.
longname : str
Verbose description of the box.
component_index : int
component_index : tuple
Index of component in codestream that is mapped to this channel.
mapping_type : int
mapping_type : tuple
mapping type, either direct use (0) or palette (1)
palette_index : int
palette_index : tuple
Index component from palette
"""
def __init__(self, component_index, mapping_type, palette_index,
@ -666,6 +698,14 @@ class ComponentMappingBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.ComponentMappingBox("
msg += "component_index={0}, mapping_type={1}, palette_index={2})"
msg = msg.format(self.component_index,
self.mapping_type,
self.palette_index)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -733,6 +773,10 @@ class ContiguousCodestreamBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.ContiguousCodeStreamBox(main_header={0})"
return msg.format(repr(self.main_header))
def __str__(self):
msg = Jp2kBox.__str__(self)
msg += '\n Main header:'
@ -801,6 +845,13 @@ class FileTypeBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.FileTypeBox(brand='{0}', minor_version={1}, "
msg += "compatibility_list={2})"
msg = msg.format(self.brand, self.minor_version,
self.compatibility_list)
return msg
def __str__(self):
lst = [Jp2kBox.__str__(self),
' Brand: {0}',
@ -914,8 +965,23 @@ class ImageHeaderBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.ImageHeaderBox("
msg += "{height}, {width}, num_components={num_components}, "
msg += "signed={signed}, bits_per_component={bits_per_component}, "
msg += "compression={compression}, "
msg += "colorspace_unknown={colorspace_unknown}, "
msg += "ip_provided={ip_provided})"
msg = msg.format(height=self.height, width=self.width,
num_components=self.num_components,
signed=self.signed,
bits_per_component=self.bits_per_component,
compression=self.compression,
colorspace_unknown=self.colorspace_unknown,
ip_provided=self.ip_provided)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
msg = "{0}"
msg += '\n Size: [{1} {2} {3}]'
msg += '\n Bitdepth: {4}'
@ -1004,11 +1070,15 @@ class AssociationBox(Jp2kBox):
box : list
List of boxes contained in this superbox.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=[], length=0, offset=-1):
Jp2kBox.__init__(self, box_id='asoc', longname='Association')
self.length = length
self.offset = offset
self.box = []
self.box = box
def __repr__(self):
msg = "glymur.jp2box.AssociationBox(box={0})".format(self.box)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -1062,11 +1132,15 @@ class JP2HeaderBox(Jp2kBox):
box : list
List of boxes contained in this superbox.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=[], length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header')
self.length = length
self.offset = offset
self.box = []
self.box = box
def __repr__(self):
msg = "glymur.jp2box.JP2HeaderBox(box={0})".format(self.box)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -1141,6 +1215,9 @@ class JPEG2000SignatureBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
return 'glymur.jp2box.JPEG2000SignatureBox()'
def __str__(self):
msg = Jp2kBox.__str__(self)
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
@ -1193,8 +1270,8 @@ class PaletteBox(Jp2kBox):
offset of the box from the start of the file.
longname : str
more verbose description of the box.
palette : list
Colormap represented as list of 1D arrays, one per color component.
palette : ndarray
Colormap array.
"""
def __init__(self, palette, bits_per_component, signed, length=0,
offset=-1):
@ -1205,10 +1282,15 @@ class PaletteBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
msg += "signed={2})"
msg = msg.format(self.palette, self.bits_per_component, self.signed)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
msg += '\n Size: ({0} x {1})'.format(len(self.palette[0]),
len(self.palette))
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
return msg
@staticmethod
@ -1238,68 +1320,29 @@ class PaletteBox(Jp2kBox):
bps = [((x & 0x07f) + 1) for x in data]
signed = [((x & 0x80) > 1) for x in data]
fmt = '>'
for bits in bps:
if bits <= 8:
fmt += 'B'
elif bits <= 16:
fmt += 'H'
elif bits <= 32:
fmt += 'I'
# Each palette component is padded out to the next largest byte.
# That means a list comprehension does this in one shot.
row_nbytes = sum([int(math.ceil(x/8.0)) for x in bps])
# Form the format string so that we can intelligently unpack the
# colormap. We have to do this because it is possible that the
# colormap columns could have different datatypes.
#
# This means that we store the palette as a list of 1D arrays,
# which reverses the usual indexing scheme.
read_buffer = fptr.read(num_entries * row_nbytes)
palette = _buffer2palette(read_buffer, num_entries, num_columns, bps)
palette = np.zeros((num_entries, num_columns), dtype=np.int32)
for j in range(num_entries):
palette[j] = struct.unpack_from(fmt, read_buffer,
offset=j * row_nbytes)
box = PaletteBox(palette, bps, signed, length=length, offset=offset)
return box
def _buffer2palette(read_buffer, num_rows, num_cols, bps):
"""Construct the palette from the buffer read from file.
Parameters
----------
read_buffer : iterable
Byte array of palette information read from file.
num_rows, num_cols : int
Size of palette.
bps : iterable
Bits per sample for each channel.
Returns
-------
palette : list of 1D arrays
Each 1D array corresponds to a channel.
"""
row_nbytes = 0
palette = []
fmt = '>'
for j in range(num_cols):
if bps[j] <= 8:
row_nbytes += 1
fmt += 'B'
palette.append(np.zeros(num_rows, dtype=np.uint8))
elif bps[j] <= 16:
row_nbytes += 2
fmt += 'H'
palette.append(np.zeros(num_rows, dtype=np.uint16))
elif bps[j] <= 32:
row_nbytes += 4
fmt += 'I'
palette.append(np.zeros(num_rows, dtype=np.uint32))
else:
msg = 'Unsupported palette bitdepth (%d).'.format(bps[j])
raise IOError(msg)
for j in range(num_rows):
row_buffer = read_buffer[(row_nbytes * j):(row_nbytes * (j + 1))]
row = struct.unpack(fmt, row_buffer)
for k in range(num_cols):
palette[k][j] = row[k]
return palette
# Map rreq codes to display text.
_READER_REQUIREMENTS_DISPLAY = {
0: 'File not completely understood',
@ -1426,6 +1469,18 @@ class ReaderRequirementsBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.ReaderRequirementsBox(fuam={fuam}, dcm={dcm}, "
msg += "standard_flag={standard_flag}, standard_mask={standard_mask}, "
msg += "vendor_feature={vendor_feature}, vendor_mask={vendor_mask})"
msg = msg.format(fuam=self.fuam,
dcm=self.dcm,
standard_flag=self.standard_flag,
standard_mask=self.standard_mask,
vendor_feature=self.vendor_feature,
vendor_mask=self.vendor_mask)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -1563,11 +1618,16 @@ class ResolutionBox(Jp2kBox):
box : list
List of boxes contained in this superbox.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=[], length=0, offset=-1):
Jp2kBox.__init__(self, box_id='res ', longname='Resolution')
self.length = length
self.offset = offset
self.box = []
self.box = box
def __repr__(self):
msg = "glymur.jp2box.ResolutionBox(box={0})"
msg = msg.format(self.box)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -1629,6 +1689,11 @@ class CaptureResolutionBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.CaptureResolutionBox({0}, {1})"
msg = msg.format(self.vertical_resolution, self.horizontal_resolution)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
msg += '\n VCR: {0}'.format(self.vertical_resolution)
@ -1686,6 +1751,11 @@ class DisplayResolutionBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.DisplayResolutionBox({0}, {1})"
msg = msg.format(self.vertical_resolution, self.horizontal_resolution)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
msg += '\n VDR: {0}'.format(self.vertical_resolution)
@ -1747,6 +1817,10 @@ class LabelBox(Jp2kBox):
msg += '\n Label: {0}'.format(self.label)
return msg
def __repr__(self):
msg = 'glymur.jp2box.LabelBox("{0}")'.format(self.label)
return msg
@staticmethod
def parse(fptr, offset, length):
"""Parse Label box.
@ -1808,6 +1882,9 @@ class XMLBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
return "glymur.jp2box.XMLBox(xml={0})".format(self.xml)
def __str__(self):
msg = Jp2kBox.__str__(self)
xml = self.xml
@ -1866,7 +1943,6 @@ class XMLBox(Jp2kBox):
msg = msg.format(offset, ude.reason)
warnings.warn(msg, UserWarning)
# Strip out any trailing nulls, as they can foul up XML parsing.
text = text.rstrip(chr(0))
@ -1906,6 +1982,10 @@ class UUIDListBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.UUIDListBox({0})".format(self.ulst)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
for j, uuid_item in enumerate(self.ulst):
@ -1957,11 +2037,15 @@ class UUIDInfoBox(Jp2kBox):
box : list
List of boxes contained in this superbox.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=[], length=0, offset=-1):
Jp2kBox.__init__(self, box_id='uinf', longname='UUIDInfo')
self.length = length
self.offset = offset
self.box = []
self.box = box
def __repr__(self):
msg = "glymur.jp2box.UUIDInfoBox(box={0})".format(self.box)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
@ -2030,6 +2114,11 @@ class DataEntryURLBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')"
msg = msg.format(self.version, self.flag, self.url)
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
msg += '\n '
@ -2112,6 +2201,7 @@ class UUIDBox(Jp2kBox):
"""
Jp2kBox.__init__(self, box_id='uuid', longname='UUID')
self.uuid = the_uuid
self.raw_data = raw_data
if the_uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
# XMP data. Parse as XML. Seems to be a difference between
@ -2145,6 +2235,12 @@ class UUIDBox(Jp2kBox):
self.length = length
self.offset = offset
def __repr__(self):
msg = "glymur.jp2box.UUIDBox(the_uuid={0}, "
msg += "raw_data=<byte array {1} elements>)"
return msg.format(repr(self.uuid), len(self.raw_data))
def __str__(self):
msg = '{0}\n'
msg += ' UUID: {1}{2}\n'

View file

@ -72,6 +72,10 @@ class Jp2k(Jp2kBox):
if mode == 'rb':
self.parse()
def __repr__(self):
msg = "glymur.Jp2k('{0}')".format(self.filename)
return msg
def __str__(self):
metadata = ['File: ' + os.path.basename(self.filename)]
if len(self.box) > 0:

View file

@ -116,5 +116,49 @@ class TestCodestream(unittest.TestCase):
# codestream, so the last one is EOC.
self.assertEqual(codestream.segment[-1].marker_id, 'EOC')
class TestCodestreamRepr(unittest.TestCase):
def setUp(self):
self.jp2file = glymur.data.nemo()
def tearDown(self):
pass
def test_soc(self):
"""Test SOC segment repr"""
segment = glymur.codestream.SOCsegment()
newseg = eval(repr(segment))
self.assertEqual(newseg.marker_id, 'SOC')
def test_siz(self):
"""Test SIZ segment repr"""
kwargs = {'rsiz': 0,
'xysiz': (2592, 1456),
'xyosiz': (0, 0),
'xytsiz': (2592, 1456),
'xytosiz': (0, 0),
'Csiz': 3,
'bitdepth': (8, 8, 8),
'signed': (False, False, False),
'xyrsiz': ((1, 1, 1), (1, 1, 1))}
segment = glymur.codestream.SIZsegment(**kwargs)
newseg = eval(repr(segment))
self.assertEqual(newseg.marker_id, 'SIZ')
self.assertEqual(newseg.xsiz, 2592)
self.assertEqual(newseg.ysiz, 1456)
self.assertEqual(newseg.xosiz, 0)
self.assertEqual(newseg.yosiz, 0)
self.assertEqual(newseg.xtsiz, 2592)
self.assertEqual(newseg.ytsiz, 1456)
self.assertEqual(newseg.xtosiz, 0)
self.assertEqual(newseg.ytosiz, 0)
self.assertEqual(newseg.xrsiz, (1, 1, 1))
self.assertEqual(newseg.yrsiz, (1, 1, 1))
self.assertEqual(newseg.bitdepth, (8, 8, 8))
self.assertEqual(newseg.signed, (False, False, False))
if __name__ == "__main__":
unittest.main()

View file

@ -18,11 +18,13 @@ Test suite specifically targeting JP2 box layout.
import doctest
import os
import re
import shutil
import struct
import sys
import tempfile
import uuid
from uuid import UUID
import xml.etree.cElementTree as ET
if sys.hexversion < 0x02070000:
@ -40,7 +42,7 @@ from glymur.jp2box import JPEG2000SignatureBox
from glymur.core import COLOR, OPACITY
from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE
from .fixtures import OPENJP2_IS_V2_OFFICIAL
from .fixtures import OPENJP2_IS_V2_OFFICIAL, opj_data_file
try:
FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT']
@ -413,7 +415,7 @@ class TestAppend(unittest.TestCase):
jp2 = Jp2k(tfile.name)
the_xml = ET.fromstring('<?xml version="1.0"?><data>0</data>')
xmlbox = glymur.jp2box.XMLBox(xml=the_xml)
xmlbox = glymur.jp2box.XMLBox(xml=ET.ElementTree(the_xml))
jp2.append(xmlbox)
# The sequence of box IDs should be the same as before, but with an
@ -709,6 +711,250 @@ class TestJp2Boxes(unittest.TestCase):
self.assertIsNone(box.main_header)
class TestRepr(unittest.TestCase):
"""Tests for __repr__ methods."""
def test_default_jp2k(self):
"""Should be able to eval a JPEG2000SignatureBox"""
jp2k = glymur.jp2box.JPEG2000SignatureBox()
# Test the representation instantiation.
newbox = eval(repr(jp2k))
self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox))
self.assertEqual(newbox.signature, (13, 10, 135, 10))
def test_default_ftyp(self):
"""Should be able to instantiate a FileTypeBox"""
ftyp = glymur.jp2box.FileTypeBox()
# Test the representation instantiation.
newbox = eval(repr(ftyp))
self.assertTrue(isinstance(newbox, glymur.jp2box.FileTypeBox))
self.assertEqual(newbox.brand, 'jp2 ')
self.assertEqual(newbox.minor_version, 0)
self.assertEqual(newbox.compatibility_list, ['jp2 '])
def test_colourspecification_box(self):
"""Verify __repr__ method on colr box."""
# TODO: add icc_profile
box = ColourSpecificationBox(colorspace=glymur.core.SRGB)
newbox = eval(repr(box))
self.assertEqual(newbox.method, glymur.core.ENUMERATED_COLORSPACE)
self.assertEqual(newbox.precedence, 0)
self.assertEqual(newbox.approximation, 0)
self.assertEqual(newbox.colorspace, glymur.core.SRGB)
self.assertIsNone(newbox.icc_profile)
def test_channeldefinition_box(self):
"""Verify __repr__ method on cdef box."""
channel_type = [COLOR, COLOR, COLOR]
association = [RED, GREEN, BLUE]
cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2],
channel_type=channel_type,
association=association)
newbox = eval(repr(cdef))
self.assertEqual(newbox.index, (0, 1, 2))
self.assertEqual(newbox.channel_type, (COLOR, COLOR, COLOR))
self.assertEqual(newbox.association, (RED, GREEN, BLUE))
def test_jp2header_box(self):
"""Verify __repr__ method on ihdr box."""
ihdr = ImageHeaderBox(100, 200, num_components=3)
colr = ColourSpecificationBox(colorspace=glymur.core.SRGB)
jp2h = JP2HeaderBox(box=[ihdr, colr])
newbox = eval(repr(jp2h))
self.assertEqual(newbox.box_id, 'jp2h')
self.assertEqual(newbox.box[0].box_id, 'ihdr')
self.assertEqual(newbox.box[1].box_id, 'colr')
def test_imageheader_box(self):
"""Verify __repr__ method on jhdr box."""
ihdr = ImageHeaderBox(100, 200, num_components=3)
newbox = eval(repr(ihdr))
self.assertEqual(newbox.height, 100)
self.assertEqual(newbox.width, 200)
self.assertEqual(newbox.num_components, 3)
self.assertFalse(newbox.signed)
self.assertEqual(newbox.bits_per_component, 8)
self.assertEqual(newbox.compression, 7)
self.assertFalse(newbox.colorspace_unknown)
self.assertFalse(newbox.ip_provided)
def test_association_box(self):
"""Verify __repr__ method on asoc box."""
asoc = glymur.jp2box.AssociationBox()
newbox = eval(repr(asoc))
self.assertEqual(newbox.box_id, 'asoc')
self.assertEqual(len(newbox.box), 0)
def test_codestreamheader_box(self):
"""Verify __repr__ method on jpch box."""
jpch = glymur.jp2box.CodestreamHeaderBox()
newbox = eval(repr(jpch))
self.assertEqual(newbox.box_id, 'jpch')
self.assertEqual(len(newbox.box), 0)
def test_compositinglayerheader_box(self):
"""Verify __repr__ method on jplh box."""
jplh = glymur.jp2box.CompositingLayerHeaderBox()
newbox = eval(repr(jplh))
self.assertEqual(newbox.box_id, 'jplh')
self.assertEqual(len(newbox.box), 0)
def test_componentmapping_box(self):
"""Verify __repr__ method on cmap box."""
cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0),
mapping_type=(1, 1, 1),
palette_index=(0, 1, 2))
newbox = eval(repr(cmap))
self.assertEqual(newbox.box_id, 'cmap')
self.assertEqual(newbox.component_index, (0, 0, 0))
self.assertEqual(newbox.mapping_type, (1, 1, 1))
self.assertEqual(newbox.palette_index, (0, 1, 2))
def test_resolution_boxes(self):
"""Verify __repr__ method on resolution boxes."""
resc = glymur.jp2box.CaptureResolutionBox(0.5, 2.5)
resd = glymur.jp2box.DisplayResolutionBox(2.5, 0.5)
res_super_box = glymur.jp2box.ResolutionBox(box=[resc, resd])
newbox = eval(repr(res_super_box))
self.assertEqual(newbox.box_id, 'res ')
self.assertEqual(newbox.box[0].box_id, 'resc')
self.assertEqual(newbox.box[0].vertical_resolution, 0.5)
self.assertEqual(newbox.box[0].horizontal_resolution, 2.5)
self.assertEqual(newbox.box[1].box_id, 'resd')
self.assertEqual(newbox.box[1].vertical_resolution, 2.5)
self.assertEqual(newbox.box[1].horizontal_resolution, 0.5)
def test_label_box(self):
"""Verify __repr__ method on label box."""
lbl = glymur.jp2box.LabelBox("this is a test")
newbox = eval(repr(lbl))
self.assertEqual(newbox.box_id, 'lbl ')
self.assertEqual(newbox.label, "this is a test")
def test_data_entry_url_box(self):
"""Verify __repr__ method on data entry url box."""
version = 0
flag = (0, 0, 0)
url = "http://readthedocs.glymur.org"
box = glymur.jp2box.DataEntryURLBox(version, flag, url)
newbox = eval(repr(box))
self.assertEqual(newbox.box_id, 'url ')
self.assertEqual(newbox.version, version)
self.assertEqual(newbox.flag, flag)
self.assertEqual(newbox.url, url)
def test_uuidinfo_box(self):
"""Verify __repr__ method on uinf box."""
uinf = glymur.jp2box.UUIDInfoBox()
newbox = eval(repr(uinf))
self.assertEqual(newbox.box_id, 'uinf')
self.assertEqual(len(newbox.box), 0)
def test_uuidlist_box(self):
"""Verify __repr__ method on ulst box."""
uuid1 = uuid.UUID('00000000-0000-0000-0000-000000000001')
uuid2 = uuid.UUID('00000000-0000-0000-0000-000000000002')
uuids = [uuid1, uuid2]
ulst = glymur.jp2box.UUIDListBox(ulst=uuids)
newbox = eval(repr(ulst))
self.assertEqual(newbox.box_id, 'ulst')
self.assertEqual(newbox.ulst[0], uuid1)
self.assertEqual(newbox.ulst[1], uuid2)
def test_jp2k_box(self):
"""Verify Superclass repr."""
box = glymur.jp2box.Jp2kBox(box_id='one', offset=2, length=3,
longname='four')
newbox = eval(repr(box))
self.assertEqual(newbox.box_id, 'one')
self.assertEqual(newbox.offset, 2)
self.assertEqual(newbox.length, 3)
self.assertEqual(newbox.longname, 'four')
def test_palette_box(self):
"""Verify Palette box repr."""
palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32)
bps = (8, 8, 16)
signed = (True, False, True)
box = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps,
signed=(True, False, True))
# The palette can't be reinstantiated thru eval/repr.
s = repr(box)
self.assertTrue(True)
@unittest.skipIf(sys.hexversion < 0x02070000, "Requires 2.7+")
def test_xml_box(self):
"""Verify xml box repr."""
elt = ET.fromstring('<?xml version="1.0"?><data>0</data>')
tree = ET.ElementTree(elt)
box = glymur.jp2box.XMLBox(xml=tree)
regexp = "glymur.jp2box.XMLBox"
regexp += "\(xml=<(xml.etree.ElementTree.){0,1}ElementTree object "
regexp += "at 0x([a-f0-9]*)>\)"
if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp)
else:
self.assertRegex(repr(box), regexp)
def test_readerrequirements_box(self):
"""Verify rreq repr method."""
box = glymur.jp2box.ReaderRequirementsBox(fuam=160, dcm=192,
standard_flag=(5, 61, 43),
standard_mask=(128, 96, 64),
vendor_feature=[],
vendor_mask=[])
newbox = eval(repr(box))
self.assertEqual(box.fuam, newbox.fuam)
self.assertEqual(box.dcm, newbox.dcm)
self.assertEqual(box.standard_flag, newbox.standard_flag)
self.assertEqual(box.standard_mask, newbox.standard_mask)
self.assertEqual(box.vendor_feature, newbox.vendor_feature)
self.assertEqual(box.vendor_mask, newbox.vendor_mask)
@unittest.skipIf(sys.hexversion < 0x02070000, "Requires 2.7+")
def test_uuid_box(self):
"""Verify uuid repr method."""
uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000')
data = b'0123456789'
box = glymur.jp2box.UUIDBox(the_uuid=uuid_instance, raw_data=data)
# Since the raw_data parameter is a sequence of bytes which could be
# quite long, don't bother trying to make it conform to eval(repr()).
regexp = "glymur.jp2box.UUIDBox\("
regexp += "the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s"
regexp += "raw_data=<byte\sarray\s10\selements>\)"
if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp)
else:
self.assertRegex(repr(box), regexp)
@unittest.skipIf(sys.hexversion < 0x02070000, "Requires 2.7+")
def test_contiguous_codestream_box(self):
"""Verify contiguous codestream box repr method."""
jp2file = glymur.data.nemo()
jp2 = Jp2k(jp2file)
box = jp2.box[-1]
# Difficult to eval(repr()) this, so just match the general pattern.
regexp = "glymur.jp2box.ContiguousCodeStreamBox"
regexp += "\(main_header=<glymur.codestream.Codestream\sobject\s"
regexp += "at\s0x([a-f0-9]*)>\)"
if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp)
else:
self.assertRegex(repr(box), regexp)
class TestJpxBoxes(unittest.TestCase):
"""Tests for JPX boxes."""

View file

@ -67,6 +67,15 @@ class TestJp2k(unittest.TestCase):
def tearDown(self):
pass
def test_repr(self):
"""Verify that results of __repr__ are eval-able."""
j = Jp2k(self.j2kfile)
newjp2 = eval(repr(j))
self.assertEqual(newjp2.filename, self.j2kfile)
self.assertEqual(newjp2.mode, 'rb')
self.assertEqual(len(newjp2.box), 0)
def test_rlevel_max(self):
"""Verify that rlevel=-1 gets us the lowest resolution image"""
j = Jp2k(self.j2kfile)

View file

@ -3797,19 +3797,16 @@ class TestSuiteDump(unittest.TestCase):
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
# Palette box.
self.assertEqual(len(jp2.box[2].box[1].palette), 3)
self.assertEqual(len(jp2.box[2].box[1].palette[0]), 256)
self.assertEqual(len(jp2.box[2].box[1].palette[1]), 256)
self.assertEqual(len(jp2.box[2].box[1].palette[2]), 256)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[0][0], 0)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[1][0], 0)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[2][0], 0)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[0][128], 73)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[1][128], 92)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[2][128], 53)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[0][-1], 245)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[1][-1], 245)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[2][-1], 245)
self.assertEqual(jp2.box[2].box[1].palette.shape, (256, 3))
np.testing.assert_array_equal(jp2.box[2].box[1].palette[0, 0], 0)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[0, 1], 0)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[0, 2], 0)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[128, 0], 73)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[128, 1], 92)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[128, 2], 53)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[255, 0], 245)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[255, 1], 245)
np.testing.assert_array_equal(jp2.box[2].box[1].palette[255, 2], 245)
# Component mapping box
self.assertEqual(jp2.box[2].box[2].component_index, (0, 0, 0))
@ -5699,10 +5696,7 @@ class TestSuiteDump(unittest.TestCase):
# Jp2 Header
# Palette box.
self.assertEqual(len(jp2.box[3].box[2].palette), 3)
self.assertEqual(len(jp2.box[3].box[2].palette[0]), 256)
self.assertEqual(len(jp2.box[3].box[2].palette[1]), 256)
self.assertEqual(len(jp2.box[3].box[2].palette[2]), 256)
self.assertEqual(jp2.box[3].box[2].palette.shape, (256, 3))
# Jp2 Header
# Component mapping box
@ -6127,11 +6121,7 @@ class TestSuiteDump(unittest.TestCase):
# Jp2 Header
# Palette box.
self.assertEqual(len(jp2.box[3].box[2].palette), 4)
self.assertEqual(len(jp2.box[3].box[2].palette[0]), 1)
self.assertEqual(len(jp2.box[3].box[2].palette[1]), 1)
self.assertEqual(len(jp2.box[3].box[2].palette[2]), 1)
self.assertEqual(len(jp2.box[3].box[2].palette[3]), 1)
self.assertEqual(jp2.box[3].box[2].palette.shape, (1, 4))
# Jp2 Header
# Component mapping box
@ -6242,10 +6232,7 @@ class TestSuiteDump(unittest.TestCase):
# Jp2 Header
# Palette box.
# 3 columns with 16 entries.
self.assertEqual(len(jp2.box[3].box[2].palette), 3)
self.assertEqual(len(jp2.box[3].box[2].palette[0]), 16)
self.assertEqual(len(jp2.box[3].box[2].palette[1]), 16)
self.assertEqual(len(jp2.box[3].box[2].palette[2]), 16)
self.assertEqual(jp2.box[3].box[2].palette.shape, (16, 3))
# Jp2 Header
# Component mapping box