Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ef8052a22 | ||
|
|
7ac25f2935 | ||
|
|
5f013fa785 | ||
|
|
ab75ffdca3 | ||
|
|
256ebbf9ec | ||
|
|
fdbbf999ca | ||
|
|
a3ba5a86ca | ||
|
|
cbdd6200b6 | ||
|
|
2516a71bc8 |
3 changed files with 420 additions and 250 deletions
427
glymur/jp2box.py
427
glymur/jp2box.py
|
|
@ -356,35 +356,45 @@ class ColourSpecificationBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method])
|
lst = []
|
||||||
msg += '\n Precedence: {0}'.format(self.precedence)
|
text = 'Method: {0}'.format(_METHOD_DISPLAY[self.method])
|
||||||
|
lst.append(text)
|
||||||
|
text = 'Precedence: {0}'.format(self.precedence)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
if self.approximation is not 0:
|
if self.approximation is not 0:
|
||||||
dispvalue = _APPROX_DISPLAY[self.approximation]
|
dispvalue = _APPROX_DISPLAY[self.approximation]
|
||||||
msg += '\n Approximation: {0}'.format(dispvalue)
|
text = 'Approximation: {0}'.format(dispvalue)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
if self.colorspace is not None:
|
if self.colorspace is not None:
|
||||||
dispvalue = _COLORSPACE_MAP_DISPLAY[self.colorspace]
|
dispvalue = _COLORSPACE_MAP_DISPLAY[self.colorspace]
|
||||||
msg += '\n Colorspace: {0}'.format(dispvalue)
|
text = 'Colorspace: {0}'.format(dispvalue)
|
||||||
else:
|
else:
|
||||||
# 2.7 has trouble pretty-printing ordered dicts so we just have
|
# 2.7 has trouble pretty-printing ordered dicts so we just have
|
||||||
# to print as a regular dict in this case.
|
# to print as a regular dict in this case.
|
||||||
if self.icc_profile is None:
|
if self.icc_profile is None:
|
||||||
msg += '\n ICC Profile: None'
|
text = 'ICC Profile: None'
|
||||||
else:
|
else:
|
||||||
if sys.hexversion < 0x03000000:
|
if sys.hexversion < 0x03000000:
|
||||||
icc_profile = dict(self.icc_profile)
|
icc_profile = dict(self.icc_profile)
|
||||||
else:
|
else:
|
||||||
icc_profile = self.icc_profile
|
icc_profile = self.icc_profile
|
||||||
dispvalue = pprint.pformat(icc_profile)
|
text = pprint.pformat(icc_profile)
|
||||||
lines = [' ' * 8 + y for y in dispvalue.split('\n')]
|
text = self._indent(text)
|
||||||
msg += '\n ICC Profile:\n{0}'.format('\n'.join(lines))
|
text = '\n'.join(['ICC Profile:', text])
|
||||||
|
|
||||||
return msg
|
lst.append(text)
|
||||||
|
|
||||||
|
text = '\n'.join(lst)
|
||||||
|
|
||||||
|
text = '\n'.join([title, self._indent(text)])
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write an Colour Specification box to file.
|
"""Write an Colour Specification box to file.
|
||||||
|
|
@ -620,19 +630,26 @@ class ChannelDefinitionBox(Jp2kBox):
|
||||||
self._dispatch_validation_error(msg, writing=writing)
|
self._dispatch_validation_error(msg, writing=writing)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
for j in range(len(self.association)):
|
for j in range(len(self.association)):
|
||||||
color_type_string = _COLOR_TYPE_MAP_DISPLAY[self.channel_type[j]]
|
color_type_string = _COLOR_TYPE_MAP_DISPLAY[self.channel_type[j]]
|
||||||
if self.association[j] == 0:
|
if self.association[j] == 0:
|
||||||
assn = 'whole image'
|
assn = 'whole image'
|
||||||
else:
|
else:
|
||||||
assn = str(self.association[j])
|
assn = str(self.association[j])
|
||||||
msg += '\n Channel {0} ({1}) ==> ({2})'
|
text = 'Channel {0} ({1}) ==> ({2})'.format(self.index[j],
|
||||||
msg = msg.format(self.index[j], color_type_string, assn)
|
color_type_string,
|
||||||
return msg
|
assn)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
return text
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.ChannelDefinitionBox("
|
msg = "glymur.jp2box.ChannelDefinitionBox("
|
||||||
|
|
@ -929,19 +946,26 @@ class ComponentMappingBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
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}'
|
text = 'Component {0} ==> palette column {1}'
|
||||||
msg = msg.format(self.component_index[k],
|
text = text.format(self.component_index[k],
|
||||||
self.palette_index[k])
|
self.palette_index[k])
|
||||||
else:
|
else:
|
||||||
msg += '\n Component {0} ==> {1}'
|
text = 'Component {0} ==> {1}'
|
||||||
msg = msg.format(self.component_index[k], k)
|
text = text.format(self.component_index[k], k)
|
||||||
return msg
|
lst.append(text)
|
||||||
|
|
||||||
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a Component Mapping box to file.
|
"""Write a Component Mapping box to file.
|
||||||
|
|
@ -1041,16 +1065,20 @@ class ContiguousCodestreamBox(Jp2kBox):
|
||||||
return msg.format(repr(self.codestream))
|
return msg.format(repr(self.codestream))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
if _printoptions['codestream'] is False:
|
if _printoptions['codestream'] is False:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
for segment in self.codestream.segment:
|
for segment in self.codestream.segment:
|
||||||
msg += '\n' + self._indent(str(segment), indent_level=4)
|
lst.append(str(segment))
|
||||||
|
|
||||||
return msg
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
return text
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset=0, length=0):
|
def parse(cls, fptr, offset=0, length=0):
|
||||||
|
|
@ -1149,13 +1177,18 @@ class DataReferenceBox(Jp2kBox):
|
||||||
fptr.seek(end_pos)
|
fptr.seek(end_pos)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
for box in self.DR:
|
for box in self.DR:
|
||||||
msg += '\n ' + str(box)
|
lst.append(str(box))
|
||||||
return msg
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
return text
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = 'glymur.jp2box.DataReferenceBox()'
|
msg = 'glymur.jp2box.DataReferenceBox()'
|
||||||
|
|
@ -1252,17 +1285,22 @@ class FileTypeBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
lst = [msg,
|
lst = []
|
||||||
' Brand: {0}',
|
text = 'Brand: {0}'.format(self.brand)
|
||||||
' Compatibility: {1}']
|
lst.append(text)
|
||||||
msg = '\n'.join(lst)
|
text = 'Compatibility: {0}'.format(self.compatibility_list)
|
||||||
msg = msg.format(self.brand, self.compatibility_list)
|
lst.append(text)
|
||||||
|
|
||||||
return msg
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
def _validate(self, writing=False):
|
def _validate(self, writing=False):
|
||||||
"""Validate the box before writing to file."""
|
"""Validate the box before writing to file."""
|
||||||
|
|
@ -1384,19 +1422,24 @@ class FragmentListBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
for j in range(len(self.fragment_offset)):
|
for j in range(len(self.fragment_offset)):
|
||||||
msg += "\n Offset {0}: {1}"
|
text = "Offset {0}: {1}".format(j, self.fragment_offset[j])
|
||||||
msg += "\n Fragment Length {2}: {3}"
|
lst.append(text)
|
||||||
msg += "\n Data Reference {4}: {5}"
|
text = "Fragment Length {0}: {1}".format(j,
|
||||||
msg = msg.format(j, self.fragment_offset[j],
|
self.fragment_length[j])
|
||||||
j, self.fragment_length[j],
|
lst.append(text)
|
||||||
j, self.data_reference[j])
|
text = "Data Reference {0}: {1}".format(j, self.data_reference[j])
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
return msg
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a fragment list box to file.
|
"""Write a fragment list box to file.
|
||||||
|
|
@ -1546,11 +1589,7 @@ class FreeBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
return Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
|
||||||
return msg
|
|
||||||
|
|
||||||
return msg
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -1642,23 +1681,34 @@ class ImageHeaderBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg = "{0}"
|
lst = []
|
||||||
msg += '\n Size: [{1} {2} {3}]'
|
|
||||||
msg += '\n Bitdepth: {4}'
|
text = 'Size: [{0} {1} {2}]'
|
||||||
msg += '\n Signed: {5}'
|
text = text.format(self.height, self.width, self.num_components)
|
||||||
msg += '\n Compression: {6}'
|
lst.append(text)
|
||||||
msg += '\n Colorspace Unknown: {7}'
|
|
||||||
msg = msg.format(Jp2kBox.__str__(self),
|
text = 'Bitdepth: {0}'.format(self.bits_per_component)
|
||||||
self.height, self.width, self.num_components,
|
lst.append(text)
|
||||||
self.bits_per_component,
|
|
||||||
self.signed,
|
text = 'Signed: {0}'.format(self.signed)
|
||||||
'wavelet' if self.compression == 7 else 'unknown',
|
lst.append(text)
|
||||||
self.colorspace_unknown)
|
|
||||||
return msg
|
text = 'Compression: {0}'
|
||||||
|
text = text.format('wavelet' if self.compression == 7 else 'unknown')
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = 'Colorspace Unknown: {0}'.format(self.colorspace_unknown)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write an Image Header box to file.
|
"""Write an Image Header box to file.
|
||||||
|
|
@ -1873,14 +1923,16 @@ class JPEG2000SignatureBox(Jp2kBox):
|
||||||
return 'glymur.jp2box.JPEG2000SignatureBox()'
|
return 'glymur.jp2box.JPEG2000SignatureBox()'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
|
body = 'Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
|
||||||
msg = msg.format(self.signature[0], self.signature[1],
|
body = body.format(self.signature[0], self.signature[1],
|
||||||
self.signature[2], self.signature[3])
|
self.signature[2], self.signature[3])
|
||||||
return msg
|
body = self._indent(body)
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a JPEG 2000 Signature box to file.
|
"""Write a JPEG 2000 Signature box to file.
|
||||||
|
|
@ -1962,12 +2014,15 @@ class PaletteBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
|
body = 'Size: ({0} x {1})'.format(*self.palette.shape)
|
||||||
return msg
|
body = self._indent(body)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a Palette box to file.
|
"""Write a Palette box to file.
|
||||||
|
|
@ -2211,25 +2266,47 @@ class ReaderRequirementsBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n Fully Understands Aspect Mask: 0x{0:x}'
|
lst = []
|
||||||
msg = msg.format(self.fuam)
|
|
||||||
msg += '\n Display Completely Mask: 0x{0:x}'.format(self.dcm)
|
|
||||||
|
|
||||||
msg += '\n Standard Features and Masks:'
|
text = 'Fully Understands Aspect Mask: 0x{0:x}'.format(self.fuam)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = 'Display Completely Mask: 0x{0:x}'.format(self.dcm)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = 'Standard Features and Masks:'
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
lst2 = []
|
||||||
for j in range(len(self.standard_flag)):
|
for j in range(len(self.standard_flag)):
|
||||||
args = (self.standard_flag[j], self.standard_mask[j],
|
args = (self.standard_flag[j], self.standard_mask[j],
|
||||||
_READER_REQUIREMENTS_DISPLAY[self.standard_flag[j]])
|
_READER_REQUIREMENTS_DISPLAY[self.standard_flag[j]])
|
||||||
msg += '\n Feature {0:03d}: 0x{1:x} {2}'.format(*args)
|
text = 'Feature {0:03d}: 0x{1:x} {2}'.format(*args)
|
||||||
|
lst2.append(text)
|
||||||
|
text = '\n'.join(lst2)
|
||||||
|
text = self._indent(text)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
msg += '\n Vendor Features:'
|
text = 'Vendor Features:'
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
lst2 = []
|
||||||
for j in range(len(self.vendor_feature)):
|
for j in range(len(self.vendor_feature)):
|
||||||
msg += '\n UUID {0}'.format(self.vendor_feature[j])
|
text = 'UUID {0}'.format(self.vendor_feature[j])
|
||||||
|
lst2.append(text)
|
||||||
|
text = '\n'.join(lst2)
|
||||||
|
text = self._indent(text)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
return msg
|
text = '\n'.join(lst)
|
||||||
|
text = self._indent(text)
|
||||||
|
text = '\n'.join([title, text])
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -2500,13 +2577,21 @@ class CaptureResolutionBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n VCR: {0}'.format(self.vertical_resolution)
|
lst = []
|
||||||
msg += '\n HCR: {0}'.format(self.horizontal_resolution)
|
text = 'VCR: {0}'.format(self.vertical_resolution)
|
||||||
return msg
|
lst.append(text)
|
||||||
|
text = 'HCR: {0}'.format(self.horizontal_resolution)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = '\n'.join(lst)
|
||||||
|
body = self._indent(text)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -2566,13 +2651,21 @@ class DisplayResolutionBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n VDR: {0}'.format(self.vertical_resolution)
|
lst = []
|
||||||
msg += '\n HDR: {0}'.format(self.horizontal_resolution)
|
text = 'VDR: {0}'.format(self.vertical_resolution)
|
||||||
return msg
|
lst.append(text)
|
||||||
|
text = 'HDR: {0}'.format(self.horizontal_resolution)
|
||||||
|
lst.append(text)
|
||||||
|
|
||||||
|
text = '\n'.join(lst)
|
||||||
|
body = self._indent(text)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -2626,12 +2719,15 @@ class LabelBox(Jp2kBox):
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n Label: {0}'.format(self.label)
|
text = 'Label: {0}'.format(self.label)
|
||||||
return msg
|
body = self._indent(text)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = 'glymur.jp2box.LabelBox("{0}")'.format(self.label)
|
msg = 'glymur.jp2box.LabelBox("{0}")'.format(self.label)
|
||||||
|
|
@ -2694,25 +2790,30 @@ class NumberListBox(Jp2kBox):
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
for j, association in enumerate(self.associations):
|
for j, association in enumerate(self.associations):
|
||||||
msg += '\n Association[{0}]: '.format(j)
|
text = 'Association[{0}]: '.format(j)
|
||||||
if association == 0:
|
if association == 0:
|
||||||
msg += 'the rendered result'
|
text += 'the rendered result'
|
||||||
elif (association >> 24) == 1:
|
elif (association >> 24) == 1:
|
||||||
idx = association & 0x00FFFFFF
|
idx = association & 0x00FFFFFF
|
||||||
msg += 'codestream {0}'
|
text += 'codestream {0}'.format(idx)
|
||||||
msg = msg.format(idx)
|
|
||||||
elif (association >> 24) == 2:
|
elif (association >> 24) == 2:
|
||||||
idx = association & 0x00FFFFFF
|
idx = association & 0x00FFFFFF
|
||||||
msg += 'compositing layer {0}'
|
text += 'compositing layer {0}'.format(idx)
|
||||||
msg = msg.format(idx)
|
|
||||||
else:
|
else:
|
||||||
msg += 'unrecognized'
|
text += 'unrecognized'
|
||||||
return msg
|
lst.append(text)
|
||||||
|
|
||||||
|
body = '\n'.join(lst)
|
||||||
|
body = self._indent(body)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = 'glymur.jp2box.NumberListBox(associations={0})'
|
msg = 'glymur.jp2box.NumberListBox(associations={0})'
|
||||||
|
|
@ -2797,21 +2898,22 @@ class XMLBox(Jp2kBox):
|
||||||
return "glymur.jp2box.XMLBox(xml={0})".format(self.xml)
|
return "glymur.jp2box.XMLBox(xml={0})".format(self.xml)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
if _printoptions['xml'] is False:
|
if _printoptions['xml'] is False:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n'
|
|
||||||
if self.xml is not None:
|
if self.xml is not None:
|
||||||
xmlstring = ET.tostring(self.xml,
|
body = ET.tostring(self.xml,
|
||||||
encoding='utf-8',
|
encoding='utf-8',
|
||||||
pretty_print=True).decode('utf-8')
|
pretty_print=True).decode('utf-8')
|
||||||
else:
|
else:
|
||||||
xmlstring = 'None'
|
body = 'None'
|
||||||
msg += self._indent(xmlstring)
|
body = self._indent(body)
|
||||||
return msg
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write an XML box to file.
|
"""Write an XML box to file.
|
||||||
|
|
@ -2918,13 +3020,19 @@ class UUIDListBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
|
lst = []
|
||||||
for j, uuid_item in enumerate(self.ulst):
|
for j, uuid_item in enumerate(self.ulst):
|
||||||
msg += '\n UUID[{0}]: {1}'.format(j, uuid_item)
|
text = 'UUID[{0}]: {1}'.format(j, uuid_item)
|
||||||
return msg
|
lst.append(text)
|
||||||
|
body = '\n'.join(lst)
|
||||||
|
body = self._indent(body)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -3070,20 +3178,21 @@ class DataEntryURLBox(Jp2kBox):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg += '\n '
|
lst = ['Version: {0}',
|
||||||
|
'Flag: {1} {2} {3}',
|
||||||
|
'URL: "{4}"']
|
||||||
|
body = '\n'.join(lst)
|
||||||
|
body = body.format(self.version,
|
||||||
|
self.flag[0], self.flag[1], self.flag[2],
|
||||||
|
self.url)
|
||||||
|
body = self._indent(body)
|
||||||
|
|
||||||
lines = ['Version: {0}',
|
text = '\n'.join([title, body])
|
||||||
'Flag: {1} {2} {3}',
|
return text
|
||||||
'URL: "{4}"']
|
|
||||||
msg += '\n '.join(lines)
|
|
||||||
msg = msg.format(self.version,
|
|
||||||
self.flag[0], self.flag[1], self.flag[2],
|
|
||||||
self.url)
|
|
||||||
return msg
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, fptr, offset, length):
|
def parse(cls, fptr, offset, length):
|
||||||
|
|
@ -3222,38 +3331,46 @@ class UUIDBox(Jp2kBox):
|
||||||
return msg.format(repr(self.uuid), len(self.raw_data))
|
return msg.format(repr(self.uuid), len(self.raw_data))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = Jp2kBox.__str__(self)
|
title = Jp2kBox.__str__(self)
|
||||||
if _printoptions['short'] is True:
|
if _printoptions['short'] is True:
|
||||||
return msg
|
return title
|
||||||
|
|
||||||
msg = '{0}\n UUID: {1}'.format(msg, self.uuid)
|
text = 'UUID: {0}'.format(self.uuid)
|
||||||
if self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
|
if self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
|
||||||
msg += ' (XMP)'
|
text += ' (XMP)'
|
||||||
elif self.uuid.bytes == b'JpgTiffExif->JP2':
|
elif self.uuid.bytes == b'JpgTiffExif->JP2':
|
||||||
msg += ' (EXIF)'
|
text += ' (EXIF)'
|
||||||
else:
|
else:
|
||||||
msg += ' (unknown)'
|
text += ' (unknown)'
|
||||||
|
|
||||||
|
lst = [text]
|
||||||
|
|
||||||
if (((_printoptions['xml'] is False) and
|
if (((_printoptions['xml'] is False) and
|
||||||
(self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
|
(self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
|
||||||
# If it's an XMP UUID, don't print the XML contents.
|
# If it's an XMP UUID, don't print the XML contents.
|
||||||
return msg
|
pass
|
||||||
|
|
||||||
if self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
|
elif self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
|
||||||
line = '\n UUID Data:\n{0}'
|
line = 'UUID Data:\n{0}'
|
||||||
xmlstring = ET.tostring(self.data,
|
xmlstring = ET.tostring(self.data,
|
||||||
encoding='utf-8',
|
encoding='utf-8',
|
||||||
pretty_print=True).decode('utf-8')
|
pretty_print=True).decode('utf-8')
|
||||||
# indent it a bit
|
# Remove any trailing newline
|
||||||
xmlstring = self._indent(xmlstring.rstrip())
|
xmlstring = xmlstring.rstrip()
|
||||||
msg += line.format(xmlstring)
|
text = line.format(xmlstring)
|
||||||
|
lst.append(text)
|
||||||
elif self.uuid.bytes == b'JpgTiffExif->JP2':
|
elif self.uuid.bytes == b'JpgTiffExif->JP2':
|
||||||
msg += '\n UUID Data: {0}'.format(str(self.data))
|
text = 'UUID Data: {0}'.format(str(self.data))
|
||||||
|
lst.append(text)
|
||||||
else:
|
else:
|
||||||
line = '\n UUID Data: {0} bytes'
|
text = 'UUID Data: {0} bytes'.format(len(self.raw_data))
|
||||||
msg += line.format(len(self.raw_data))
|
lst.append(text)
|
||||||
|
|
||||||
return msg
|
body = '\n'.join(lst)
|
||||||
|
body = self._indent(body)
|
||||||
|
|
||||||
|
text = '\n'.join([title, body])
|
||||||
|
return text
|
||||||
|
|
||||||
def write(self, fptr):
|
def write(self, fptr):
|
||||||
"""Write a UUID box to file.
|
"""Write a UUID box to file.
|
||||||
|
|
|
||||||
242
glymur/jp2k.py
242
glymur/jp2k.py
|
|
@ -363,7 +363,11 @@ class Jp2k(Jp2kBox):
|
||||||
# 2.1 API
|
# 2.1 API
|
||||||
self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_4K
|
self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_4K
|
||||||
|
|
||||||
def _populate_cparams(self, img_array, **kwargs):
|
def _populate_cparams(self, img_array, mct=None, cratios=None, psnr=None,
|
||||||
|
cinema2k=None, cinema4k=None, irreversible=None,
|
||||||
|
cbsize=None, eph=None, grid_offset=None, modesw=None,
|
||||||
|
numres=None, prog=None, psizes=None, sop=None,
|
||||||
|
subsam=None, tilesize=None, colorspace=None):
|
||||||
"""Directs processing of write method arguments.
|
"""Directs processing of write method arguments.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -373,12 +377,14 @@ class Jp2k(Jp2kBox):
|
||||||
kwargs : dictionary
|
kwargs : dictionary
|
||||||
non-image keyword inputs provided to write method
|
non-image keyword inputs provided to write method
|
||||||
"""
|
"""
|
||||||
if ((('cinema2k' in kwargs or 'cinema4k' in kwargs) and
|
other_args = (mct, cratios, psnr, irreversible, cbsize, eph,
|
||||||
(len(set(kwargs)) > 1))):
|
grid_offset, modesw, numres, prog, psizes, sop, subsam)
|
||||||
|
if (((cinema2k is not None or cinema4k is not None) and
|
||||||
|
(not all([arg is None for arg in other_args])))):
|
||||||
msg = "Cannot specify cinema2k/cinema4k along with other options."
|
msg = "Cannot specify cinema2k/cinema4k along with other options."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
if 'cratios' in kwargs and 'psnr' in kwargs:
|
if cratios is not None and psnr is not None:
|
||||||
msg = "Cannot specify cratios and psnr together."
|
msg = "Cannot specify cratios and psnr together."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
|
@ -402,90 +408,81 @@ class Jp2k(Jp2kBox):
|
||||||
cparams.tcp_numlayers = 1
|
cparams.tcp_numlayers = 1
|
||||||
cparams.cp_disto_alloc = 1
|
cparams.cp_disto_alloc = 1
|
||||||
|
|
||||||
if 'irreversible' in kwargs and kwargs['irreversible'] is True:
|
cparams.irreversible = 1 if irreversible else 0
|
||||||
cparams.irreversible = 1
|
|
||||||
|
|
||||||
if 'cinema2k' in kwargs:
|
if cinema2k is not None:
|
||||||
self._cparams = cparams
|
self._cparams = cparams
|
||||||
self._set_cinema_params('cinema2k', kwargs['cinema2k'])
|
self._set_cinema_params('cinema2k', cinema2k)
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'cinema4k' in kwargs:
|
if cinema4k is not None:
|
||||||
self._cparams = cparams
|
self._cparams = cparams
|
||||||
self._set_cinema_params('cinema4k', kwargs['cinema4k'])
|
self._set_cinema_params('cinema4k', cinema4k)
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'cbsize' in kwargs:
|
if cbsize is not None:
|
||||||
cparams.cblockw_init = kwargs['cbsize'][1]
|
cparams.cblockw_init = cbsize[1]
|
||||||
cparams.cblockh_init = kwargs['cbsize'][0]
|
cparams.cblockh_init = cbsize[0]
|
||||||
|
|
||||||
if 'cratios' in kwargs:
|
if cratios is not None:
|
||||||
cparams.tcp_numlayers = len(kwargs['cratios'])
|
cparams.tcp_numlayers = len(cratios)
|
||||||
for j, cratio in enumerate(kwargs['cratios']):
|
for j, cratio in enumerate(cratios):
|
||||||
cparams.tcp_rates[j] = cratio
|
cparams.tcp_rates[j] = cratio
|
||||||
cparams.cp_disto_alloc = 1
|
cparams.cp_disto_alloc = 1
|
||||||
|
|
||||||
if 'eph' in kwargs:
|
cparams.csty |= 0x02 if sop else 0
|
||||||
cparams.csty |= 0x04
|
cparams.csty |= 0x04 if eph else 0
|
||||||
|
|
||||||
if 'grid_offset' in kwargs:
|
if grid_offset is not None:
|
||||||
cparams.image_offset_x0 = kwargs['grid_offset'][1]
|
cparams.image_offset_x0 = grid_offset[1]
|
||||||
cparams.image_offset_y0 = kwargs['grid_offset'][0]
|
cparams.image_offset_y0 = grid_offset[0]
|
||||||
|
|
||||||
if 'modesw' in kwargs:
|
if modesw is not None:
|
||||||
for shift in range(6):
|
for shift in range(6):
|
||||||
power_of_two = 1 << shift
|
power_of_two = 1 << shift
|
||||||
if kwargs['modesw'] & power_of_two:
|
if modesw & power_of_two:
|
||||||
cparams.mode |= power_of_two
|
cparams.mode |= power_of_two
|
||||||
|
|
||||||
if 'numres' in kwargs:
|
if numres is not None:
|
||||||
cparams.numresolution = kwargs['numres']
|
cparams.numresolution = numres
|
||||||
|
|
||||||
if 'prog' in kwargs:
|
if prog is not None:
|
||||||
prog = kwargs['prog'].upper()
|
cparams.prog_order = core.PROGRESSION_ORDER[prog.upper()]
|
||||||
cparams.prog_order = core.PROGRESSION_ORDER[prog]
|
|
||||||
|
|
||||||
if 'psnr' in kwargs:
|
if psnr is not None:
|
||||||
cparams.tcp_numlayers = len(kwargs['psnr'])
|
cparams.tcp_numlayers = len(psnr)
|
||||||
for j, snr_layer in enumerate(kwargs['psnr']):
|
for j, snr_layer in enumerate(psnr):
|
||||||
cparams.tcp_distoratio[j] = snr_layer
|
cparams.tcp_distoratio[j] = snr_layer
|
||||||
cparams.cp_fixed_quality = 1
|
cparams.cp_fixed_quality = 1
|
||||||
|
|
||||||
if 'psizes' in kwargs:
|
if psizes is not None:
|
||||||
for j, (prch, prcw) in enumerate(kwargs['psizes']):
|
for j, (prch, prcw) in enumerate(psizes):
|
||||||
cparams.prcw_init[j] = prcw
|
cparams.prcw_init[j] = prcw
|
||||||
cparams.prch_init[j] = prch
|
cparams.prch_init[j] = prch
|
||||||
cparams.csty |= 0x01
|
cparams.csty |= 0x01
|
||||||
cparams.res_spec = len(kwargs['psizes'])
|
cparams.res_spec = len(psizes)
|
||||||
|
|
||||||
if 'sop' in kwargs:
|
if subsam is not None:
|
||||||
cparams.csty |= 0x02
|
cparams.subsampling_dy = subsam[0]
|
||||||
|
cparams.subsampling_dx = subsam[1]
|
||||||
|
|
||||||
if 'subsam' in kwargs:
|
if tilesize is not None:
|
||||||
cparams.subsampling_dy = kwargs['subsam'][0]
|
cparams.cp_tdx = tilesize[1]
|
||||||
cparams.subsampling_dx = kwargs['subsam'][1]
|
cparams.cp_tdy = tilesize[0]
|
||||||
|
|
||||||
if 'tilesize' in kwargs:
|
|
||||||
cparams.cp_tdx = kwargs['tilesize'][1]
|
|
||||||
cparams.cp_tdy = kwargs['tilesize'][0]
|
|
||||||
cparams.tile_size_on = opj2.TRUE
|
cparams.tile_size_on = opj2.TRUE
|
||||||
|
|
||||||
try:
|
if mct is None:
|
||||||
mct = kwargs['mct']
|
# If the multi component transform was not specified, we infer
|
||||||
if mct and self._colorspace == opj2.CLRSPC_GRAY:
|
# that it should be used if the color space is RGB.
|
||||||
|
cparams.tcp_mct = 1 if self._colorspace == opj2.CLRSPC_SRGB else 0
|
||||||
|
else:
|
||||||
|
if self._colorspace == opj2.CLRSPC_GRAY:
|
||||||
msg = "Cannot specify usage of the multi component transform "
|
msg = "Cannot specify usage of the multi component transform "
|
||||||
msg += "if the colorspace is gray."
|
msg += "if the colorspace is gray."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
cparams.tcp_mct = 1 if mct else 0
|
cparams.tcp_mct = 1 if mct else 0
|
||||||
except KeyError:
|
|
||||||
# If the multi component transform was not specified, we infer
|
|
||||||
# that it should be used if the color space is RGB.
|
|
||||||
if self._colorspace == opj2.CLRSPC_SRGB:
|
|
||||||
cparams.tcp_mct = 1
|
|
||||||
else:
|
|
||||||
cparams.tcp_mct = 0
|
|
||||||
|
|
||||||
self._validate_compression_params(img_array, cparams, **kwargs)
|
self._validate_compression_params(img_array, cparams, colorspace)
|
||||||
|
|
||||||
self._cparams = cparams
|
self._cparams = cparams
|
||||||
|
|
||||||
|
|
@ -576,29 +573,26 @@ class Jp2k(Jp2kBox):
|
||||||
|
|
||||||
self.parse()
|
self.parse()
|
||||||
|
|
||||||
def _validate_compression_params(self, img_array, cparams, **kwargs):
|
def _validate_j2k_colorspace(self, cparams, colorspace):
|
||||||
"""Check that the compression parameters are valid.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
img_array : ndarray
|
|
||||||
Image data to be written to file.
|
|
||||||
cparams : CompressionParametersType(ctypes.Structure)
|
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
|
||||||
"""
|
"""
|
||||||
# Cannot specify a colorspace with J2K.
|
Cannot specify a colorspace with J2K.
|
||||||
if cparams.codec_fmt == opj2.CODEC_J2K and 'colorspace' in kwargs:
|
"""
|
||||||
|
if cparams.codec_fmt == opj2.CODEC_J2K and colorspace is not None:
|
||||||
msg = 'Do not specify a colorspace when writing a raw '
|
msg = 'Do not specify a colorspace when writing a raw '
|
||||||
msg += 'codestream.'
|
msg += 'codestream.'
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
# Code block size
|
def _validate_codeblock_size(self, cparams):
|
||||||
code_block_specified = False
|
"""
|
||||||
|
Code block dimensions must satisfy certain restrictions.
|
||||||
|
|
||||||
|
They must both be a power of 2 and the total area defined by the width
|
||||||
|
and height cannot be either too great or too small for the codec.
|
||||||
|
"""
|
||||||
if cparams.cblockw_init != 0 and cparams.cblockh_init != 0:
|
if cparams.cblockw_init != 0 and cparams.cblockh_init != 0:
|
||||||
# These fields ARE zero if uninitialized.
|
# These fields ARE zero if uninitialized.
|
||||||
width = cparams.cblockw_init
|
width = cparams.cblockw_init
|
||||||
height = cparams.cblockh_init
|
height = cparams.cblockh_init
|
||||||
code_block_specified = True
|
|
||||||
if height * width > 4096 or height < 4 or width < 4:
|
if height * width > 4096 or height < 4 or width < 4:
|
||||||
msg = "Code block area cannot exceed 4096. "
|
msg = "Code block area cannot exceed 4096. "
|
||||||
msg += "Code block height and width must be larger than 4."
|
msg += "Code block height and width must be larger than 4."
|
||||||
|
|
@ -609,7 +603,17 @@ class Jp2k(Jp2kBox):
|
||||||
msg += "must be powers of 2."
|
msg += "must be powers of 2."
|
||||||
raise IOError(msg.format(height, width))
|
raise IOError(msg.format(height, width))
|
||||||
|
|
||||||
# Precinct size
|
def _validate_precinct_size(self, cparams):
|
||||||
|
"""
|
||||||
|
Precinct dimensions must satisfy certain restrictions if specified.
|
||||||
|
|
||||||
|
They must both be a power of 2 and must both be at least twice the
|
||||||
|
size of their codeblock size counterparts.
|
||||||
|
"""
|
||||||
|
code_block_specified = False
|
||||||
|
if cparams.cblockw_init != 0 and cparams.cblockh_init != 0:
|
||||||
|
code_block_specified = True
|
||||||
|
|
||||||
if cparams.res_spec != 0:
|
if cparams.res_spec != 0:
|
||||||
# precinct size was not specified if this field is zero.
|
# precinct size was not specified if this field is zero.
|
||||||
for j in range(cparams.res_spec):
|
for j in range(cparams.res_spec):
|
||||||
|
|
@ -627,11 +631,18 @@ class Jp2k(Jp2kBox):
|
||||||
msg += "must be powers of 2."
|
msg += "must be powers of 2."
|
||||||
raise IOError(msg.format(prch, prcw))
|
raise IOError(msg.format(prch, prcw))
|
||||||
|
|
||||||
# What would the point of 1D images be?
|
def _validate_image_rank(self, img_array):
|
||||||
|
"""
|
||||||
|
Images must be either 2D or 3D.
|
||||||
|
"""
|
||||||
if img_array.ndim == 1 or img_array.ndim > 3:
|
if img_array.ndim == 1 or img_array.ndim > 3:
|
||||||
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
def _validate_v2_0_0_images(self, img_array):
|
||||||
|
"""
|
||||||
|
Version 2.0.0 is restricted to only the most common images.
|
||||||
|
"""
|
||||||
if re.match("2.0.0", version.openjpeg_version) is not None:
|
if re.match("2.0.0", version.openjpeg_version) is not None:
|
||||||
if (((img_array.ndim != 2) and
|
if (((img_array.ndim != 2) and
|
||||||
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
||||||
|
|
@ -641,11 +652,32 @@ class Jp2k(Jp2kBox):
|
||||||
msg += "release."
|
msg += "release."
|
||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
|
def _validate_image_datatype(self, img_array):
|
||||||
|
"""
|
||||||
|
Only uint8 and uint16 images are currently supported.
|
||||||
|
"""
|
||||||
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
|
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
|
||||||
msg = "Only uint8 and uint16 datatypes are currently supported "
|
msg = "Only uint8 and uint16 datatypes are currently supported "
|
||||||
msg += "when writing."
|
msg += "when writing."
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
def _validate_compression_params(self, img_array, cparams, colorspace):
|
||||||
|
"""Check that the compression parameters are valid.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
img_array : ndarray
|
||||||
|
Image data to be written to file.
|
||||||
|
cparams : CompressionParametersType(ctypes.Structure)
|
||||||
|
Corresponds to cparameters_t type in openjp2 headers.
|
||||||
|
"""
|
||||||
|
self._validate_j2k_colorspace(cparams, colorspace)
|
||||||
|
self._validate_codeblock_size(cparams)
|
||||||
|
self._validate_precinct_size(cparams)
|
||||||
|
self._validate_image_rank(img_array)
|
||||||
|
self._validate_v2_0_0_images(img_array)
|
||||||
|
self._validate_image_datatype(img_array)
|
||||||
|
|
||||||
def _determine_colorspace(self, colorspace=None, **kwargs):
|
def _determine_colorspace(self, colorspace=None, **kwargs):
|
||||||
"""Determine the colorspace from the supplied inputs.
|
"""Determine the colorspace from the supplied inputs.
|
||||||
|
|
||||||
|
|
@ -916,6 +948,47 @@ class Jp2k(Jp2kBox):
|
||||||
msg = "Partial write operations are currently not allowed."
|
msg = "Partial write operations are currently not allowed."
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
def _remove_ellipsis(self, index, numrows, numcols, numbands):
|
||||||
|
"""
|
||||||
|
resolve the first ellipsis in the index so that it references the image
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
index : tuple
|
||||||
|
tuple of index arguments, presumably one of them is the Ellipsis
|
||||||
|
numrows, numcols, numbands : int
|
||||||
|
image dimensions
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
newindex : tuple
|
||||||
|
Same as index, except that the first Ellipsis is replaced with
|
||||||
|
a proper slice whose start and stop members are not None
|
||||||
|
"""
|
||||||
|
# Remove the first ellipsis we find.
|
||||||
|
rows = slice(0, numrows)
|
||||||
|
cols = slice(0, numcols)
|
||||||
|
bands = slice(0, numbands)
|
||||||
|
if index[0] is Ellipsis:
|
||||||
|
if len(index) == 2:
|
||||||
|
# jp2k[..., other_slice]
|
||||||
|
newindex = (rows, cols, index[1])
|
||||||
|
else:
|
||||||
|
# jp2k[..., cols, bands]
|
||||||
|
newindex = (rows, index[1], index[2])
|
||||||
|
elif index[1] is Ellipsis:
|
||||||
|
if len(index) == 2:
|
||||||
|
# jp2k[rows, ...]
|
||||||
|
newindex = (index[0], cols, bands)
|
||||||
|
else:
|
||||||
|
# jp2k[rows, ..., bands]
|
||||||
|
newindex = (index[0], cols, index[2])
|
||||||
|
else:
|
||||||
|
# Assume that we don't have 4D imagery, of course.
|
||||||
|
newindex = (index[0], index[1], bands)
|
||||||
|
|
||||||
|
return newindex
|
||||||
|
|
||||||
def __getitem__(self, pargs):
|
def __getitem__(self, pargs):
|
||||||
"""
|
"""
|
||||||
Slicing protocol.
|
Slicing protocol.
|
||||||
|
|
@ -950,23 +1023,7 @@ class Jp2k(Jp2kBox):
|
||||||
pargs = (pargs, Ellipsis)
|
pargs = (pargs, Ellipsis)
|
||||||
|
|
||||||
if isinstance(pargs, tuple) and any(x is Ellipsis for x in pargs):
|
if isinstance(pargs, tuple) and any(x is Ellipsis for x in pargs):
|
||||||
# Remove the first ellipsis we find.
|
newindex = self._remove_ellipsis(pargs, numrows, numcols, numbands)
|
||||||
rows = slice(0, numrows)
|
|
||||||
cols = slice(0, numcols)
|
|
||||||
bands = slice(0, numbands)
|
|
||||||
if pargs[0] is Ellipsis:
|
|
||||||
if len(pargs) == 2:
|
|
||||||
newindex = (rows, cols, pargs[1])
|
|
||||||
else:
|
|
||||||
newindex = (rows, pargs[1], pargs[2])
|
|
||||||
elif pargs[1] is Ellipsis:
|
|
||||||
if len(pargs) == 2:
|
|
||||||
newindex = (pargs[0], cols, bands)
|
|
||||||
else:
|
|
||||||
newindex = (pargs[0], cols, pargs[2])
|
|
||||||
else:
|
|
||||||
# Assume that we don't have 4D imagery, of course.
|
|
||||||
newindex = (pargs[0], pargs[1], bands)
|
|
||||||
|
|
||||||
# Run once again because it is possible that there's another
|
# Run once again because it is possible that there's another
|
||||||
# Ellipsis object in the 2nd or 3rd position.
|
# Ellipsis object in the 2nd or 3rd position.
|
||||||
|
|
@ -1279,12 +1336,11 @@ class Jp2k(Jp2kBox):
|
||||||
infile += b'0' * nelts
|
infile += b'0' * nelts
|
||||||
dparam.infile = infile
|
dparam.infile = infile
|
||||||
|
|
||||||
if self.ignore_pclr_cmap_cdef:
|
# Return raw codestream components instead of "interpolating" the
|
||||||
# Return raw codestream components.
|
# colormap?
|
||||||
dparam.flags |= 1
|
dparam.flags |= 1 if self.ignore_pclr_cmap_cdef else 0
|
||||||
|
|
||||||
dparam.decod_format = self._codec_format
|
dparam.decod_format = self._codec_format
|
||||||
|
|
||||||
dparam.cp_layer = self._layer
|
dparam.cp_layer = self._layer
|
||||||
|
|
||||||
# Must check the specified rlevel against the maximum.
|
# Must check the specified rlevel against the maximum.
|
||||||
|
|
@ -1315,10 +1371,6 @@ class Jp2k(Jp2kBox):
|
||||||
dparam.tile_index = tile
|
dparam.tile_index = tile
|
||||||
dparam.nb_tile_to_decode = 1
|
dparam.nb_tile_to_decode = 1
|
||||||
|
|
||||||
if self.ignore_pclr_cmap_cdef:
|
|
||||||
# Return raw codestream components.
|
|
||||||
dparam.flags |= 1
|
|
||||||
|
|
||||||
self._dparams = dparam
|
self._dparams = dparam
|
||||||
|
|
||||||
def read_bands(self, rlevel=0, layer=None, area=None, tile=None,
|
def read_bands(self, rlevel=0, layer=None, area=None, tile=None,
|
||||||
|
|
|
||||||
|
|
@ -1125,6 +1125,7 @@ class TestJp2dump(unittest.TestCase):
|
||||||
|
|
||||||
def test_suppress_xml(self):
|
def test_suppress_xml(self):
|
||||||
"""Verify dumping with -x, suppress XML."""
|
"""Verify dumping with -x, suppress XML."""
|
||||||
|
self.maxDiff = None
|
||||||
actual = self.run_jp2dump(['', '-x', self.jp2file])
|
actual = self.run_jp2dump(['', '-x', self.jp2file])
|
||||||
|
|
||||||
# shave off the XML and non-main-header segments
|
# shave off the XML and non-main-header segments
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue