Merge branch 'issue130' into devel
This commit is contained in:
commit
0c8b11b722
3 changed files with 99 additions and 110 deletions
|
|
@ -380,13 +380,13 @@ class Codestream(object):
|
|||
COD segment instance.
|
||||
"""
|
||||
offset = fptr.tell() - 2
|
||||
offset = fptr.tell() - 2
|
||||
|
||||
read_buffer = fptr.read(3)
|
||||
length, scod = struct.unpack('>HB', read_buffer)
|
||||
read_buffer = fptr.read(2)
|
||||
length, = struct.unpack('>H', read_buffer)
|
||||
|
||||
numbytes = offset + 2 + length - fptr.tell()
|
||||
spcod = fptr.read(numbytes)
|
||||
read_buffer = fptr.read(length - 2)
|
||||
scod, = struct.unpack_from('>B', read_buffer, offset=0)
|
||||
spcod = read_buffer[1:]
|
||||
spcod = np.frombuffer(spcod, dtype=np.uint8)
|
||||
if spcod[0] not in [LRCP, RLCP, RPCL, PCRL, CPRL]:
|
||||
msg = "Invalid progression order in COD segment: {0}."
|
||||
|
|
@ -583,22 +583,21 @@ class Codestream(object):
|
|||
read_buffer = fptr.read(2)
|
||||
length, = struct.unpack('>H', read_buffer)
|
||||
|
||||
read_buffer = fptr.read(length - 2)
|
||||
if self._csiz > 256:
|
||||
read_buffer = fptr.read(3)
|
||||
fmt = '>HB'
|
||||
mantissa_exponent_buffer_length = length - 5
|
||||
mantissa_exponent_offset = 3
|
||||
else:
|
||||
read_buffer = fptr.read(2)
|
||||
fmt = '>BB'
|
||||
mantissa_exponent_buffer_length = length - 4
|
||||
cqcc, sqcc = struct.unpack(fmt, read_buffer)
|
||||
mantissa_exponent_offset = 2
|
||||
cqcc, sqcc = struct.unpack_from(fmt, read_buffer)
|
||||
if cqcc >= self._csiz:
|
||||
msg = "Invalid component number ({0}), "
|
||||
msg += "number of components is only {1}."
|
||||
msg = msg.format(cqcc, self._csiz)
|
||||
warnings.warn(msg)
|
||||
|
||||
spqcc = fptr.read(mantissa_exponent_buffer_length)
|
||||
spqcc = read_buffer[mantissa_exponent_offset:]
|
||||
|
||||
return QCCsegment(cqcc, sqcc, spqcc, length, offset)
|
||||
|
||||
|
|
@ -670,8 +669,8 @@ class Codestream(object):
|
|||
read_buffer = fptr.read(2)
|
||||
length, = struct.unpack('>H', read_buffer)
|
||||
|
||||
xy_buffer = fptr.read(36)
|
||||
data = struct.unpack('>HIIIIIIIIH', xy_buffer)
|
||||
read_buffer = fptr.read(length - 2)
|
||||
data = struct.unpack_from('>HIIIIIIIIH', read_buffer)
|
||||
|
||||
rsiz = data[0]
|
||||
if rsiz not in _KNOWN_PROFILES:
|
||||
|
|
@ -685,9 +684,8 @@ class Codestream(object):
|
|||
# Csiz is the number of components
|
||||
Csiz = data[9]
|
||||
|
||||
component_buffer = fptr.read(Csiz * 3)
|
||||
data = struct.unpack('>' + 'B' * len(component_buffer),
|
||||
component_buffer)
|
||||
data = struct.unpack_from('>' + 'B' * (length - 36 - 2),
|
||||
read_buffer, offset=36)
|
||||
|
||||
bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3])
|
||||
signed = tuple(((x & 0x80) > 0) for x in data[0::3])
|
||||
|
|
@ -804,8 +802,8 @@ class Codestream(object):
|
|||
read_buffer = fptr.read(2)
|
||||
length, = struct.unpack('>H', read_buffer)
|
||||
|
||||
read_buffer = fptr.read(2)
|
||||
ztlm, stlm = struct.unpack('>BB', read_buffer)
|
||||
read_buffer = fptr.read(length - 2)
|
||||
ztlm, stlm = struct.unpack_from('>BB', read_buffer)
|
||||
ttlm_st = (stlm >> 4) & 0x3
|
||||
ptlm_sp = (stlm >> 6) & 0x1
|
||||
|
||||
|
|
@ -815,7 +813,6 @@ class Codestream(object):
|
|||
else:
|
||||
ntiles = nbytes / (ttlm_st + (ptlm_sp + 1) * 2)
|
||||
|
||||
read_buffer = fptr.read(nbytes)
|
||||
if ttlm_st == 0:
|
||||
ttlm = None
|
||||
fmt = ''
|
||||
|
|
@ -829,7 +826,8 @@ class Codestream(object):
|
|||
else:
|
||||
fmt += 'I'
|
||||
|
||||
data = struct.unpack('>' + fmt * int(ntiles), read_buffer)
|
||||
data = struct.unpack_from('>' + fmt * int(ntiles), read_buffer,
|
||||
offset=2)
|
||||
if ttlm_st == 0:
|
||||
ttlm = None
|
||||
ptlm = data
|
||||
|
|
|
|||
106
glymur/jp2box.py
106
glymur/jp2box.py
|
|
@ -43,7 +43,7 @@ _METHOD_DISPLAY = {
|
|||
ANY_ICC_PROFILE: 'any ICC profile',
|
||||
VENDOR_COLOR_METHOD: 'vendor color method'}
|
||||
|
||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||
_APPROX_DISPLAY = _Keydefaultdict(_factory,
|
||||
{1: 'accurately represents correct colorspace definition',
|
||||
2: 'approximates correct colorspace definition, exceptional quality',
|
||||
|
|
@ -125,7 +125,7 @@ class Jp2kBox(object):
|
|||
String to be indented.
|
||||
indent_level : str
|
||||
Number of spaces of indentation to add.
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
indented_string : str
|
||||
|
|
@ -407,15 +407,16 @@ class ColourSpecificationBox(Jp2kBox):
|
|||
-------
|
||||
ColourSpecificationBox instance
|
||||
"""
|
||||
num_bytes = offset + length - fptr.tell()
|
||||
read_buffer = fptr.read(num_bytes)
|
||||
# Read the brand, minor version.
|
||||
read_buffer = fptr.read(3)
|
||||
(method, precedence, approximation) = struct.unpack('>BBB',
|
||||
read_buffer)
|
||||
(method, precedence, approximation) = struct.unpack_from('>BBB',
|
||||
read_buffer,
|
||||
offset=0)
|
||||
|
||||
if method == 1:
|
||||
# enumerated colour space
|
||||
read_buffer = fptr.read(4)
|
||||
colorspace, = struct.unpack('>I', read_buffer)
|
||||
colorspace, = struct.unpack_from('>I', read_buffer, offset=3)
|
||||
if colorspace not in _COLORSPACE_MAP_DISPLAY.keys():
|
||||
msg = "Unrecognized colorspace: {0}".format(colorspace)
|
||||
warnings.warn(msg)
|
||||
|
|
@ -424,14 +425,13 @@ class ColourSpecificationBox(Jp2kBox):
|
|||
else:
|
||||
# ICC profile
|
||||
colorspace = None
|
||||
numbytes = offset + length - fptr.tell()
|
||||
if numbytes < 128:
|
||||
if (num_bytes - 3) < 128:
|
||||
msg = "ICC profile header is corrupt, length is "
|
||||
msg += "only {0} instead of 128."
|
||||
warnings.warn(msg.format(numbytes), UserWarning)
|
||||
warnings.warn(msg.format(num_bytes - 3), UserWarning)
|
||||
icc_profile = None
|
||||
else:
|
||||
profile = _ICCProfile(fptr.read(numbytes))
|
||||
profile = _ICCProfile(read_buffer[3:])
|
||||
icc_profile = profile.header
|
||||
|
||||
return cls(method=method,
|
||||
|
|
@ -659,12 +659,14 @@ class ChannelDefinitionBox(Jp2kBox):
|
|||
-------
|
||||
ComponentDefinitionBox instance
|
||||
"""
|
||||
# Read the number of components.
|
||||
read_buffer = fptr.read(2)
|
||||
num_components, = struct.unpack('>H', read_buffer)
|
||||
num_bytes = offset + length - fptr.tell()
|
||||
read_buffer = fptr.read(num_bytes)
|
||||
|
||||
read_buffer = fptr.read(num_components * 6)
|
||||
data = struct.unpack('>' + 'HHH' * num_components, read_buffer)
|
||||
# Read the number of components.
|
||||
num_components, = struct.unpack_from('>H', read_buffer)
|
||||
|
||||
data = struct.unpack_from('>' + 'HHH' * num_components, read_buffer,
|
||||
offset=2)
|
||||
index = data[0:num_components * 6:3]
|
||||
channel_type = data[1:num_components * 6:3]
|
||||
association = data[2:num_components * 6:3]
|
||||
|
|
@ -1103,7 +1105,7 @@ class DataReferenceBox(Jp2kBox):
|
|||
|
||||
@classmethod
|
||||
def parse(cls, fptr, offset, length):
|
||||
"""Parse Label box.
|
||||
"""Parse data reference box.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -1234,19 +1236,17 @@ class FileTypeBox(Jp2kBox):
|
|||
-------
|
||||
FileTypeBox instance
|
||||
"""
|
||||
# Read the brand, minor version.
|
||||
read_buffer = fptr.read(8)
|
||||
(brand, minor_version) = struct.unpack('>4sI', read_buffer)
|
||||
read_buffer = fptr.read(length - 8)
|
||||
# Extract the brand, minor version.
|
||||
(brand, minor_version) = struct.unpack_from('>4sI', read_buffer, 0)
|
||||
if sys.hexversion >= 0x030000:
|
||||
brand = brand.decode('utf-8')
|
||||
|
||||
# Read the compatibility list. Each entry has 4 bytes.
|
||||
current_pos = fptr.tell()
|
||||
num_bytes = (offset + length - current_pos) / 4
|
||||
read_buffer = fptr.read(int(num_bytes) * 4)
|
||||
# Extract the compatibility list. Each entry has 4 bytes.
|
||||
num_entries = int((length - 16)/ 4)
|
||||
compatibility_list = []
|
||||
for j in range(int(num_bytes)):
|
||||
entry, = struct.unpack('>4s', read_buffer[4*j:4*(j+1)])
|
||||
for j in range(int(num_entries)):
|
||||
entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4)
|
||||
if sys.hexversion >= 0x03000000:
|
||||
entry = entry.decode('utf-8')
|
||||
compatibility_list.append(entry)
|
||||
|
|
@ -1348,11 +1348,13 @@ class FragmentListBox(Jp2kBox):
|
|||
-------
|
||||
FragmentListBox instance
|
||||
"""
|
||||
read_buffer = fptr.read(2)
|
||||
num_fragments, = struct.unpack('>H', read_buffer)
|
||||
num_bytes = offset + length - fptr.tell()
|
||||
read_buffer = fptr.read(num_bytes)
|
||||
num_fragments, = struct.unpack_from('>H', read_buffer, offset=0)
|
||||
|
||||
read_buffer = fptr.read(num_fragments * 14)
|
||||
lst = struct.unpack('>' + 'QIH' * num_fragments, read_buffer)
|
||||
lst = struct.unpack_from('>' + 'QIH' * num_fragments,
|
||||
read_buffer,
|
||||
offset=2)
|
||||
frag_offset = lst[0::3]
|
||||
frag_len = lst[1::3]
|
||||
data_reference = lst[2::3]
|
||||
|
|
@ -1885,11 +1887,11 @@ class PaletteBox(Jp2kBox):
|
|||
if all(b == bps[0] for b in bps):
|
||||
# All components are the same. Writing is straightforward.
|
||||
if self.bits_per_component[0] <= 8:
|
||||
write_buffer = np.getbuffer(self.palette.astype(np.uint8))
|
||||
write_buffer = memoryview(self.palette.astype(np.uint8))
|
||||
elif self.bits_per_component[0] <= 16:
|
||||
write_buffer = np.getbuffer(self.palette.astype(np.uint16))
|
||||
write_buffer = memoryview(self.palette.astype(np.uint16))
|
||||
elif self.bits_per_component[0] <= 32:
|
||||
write_buffer = np.getbuffer(self.palette.astype(np.uint32))
|
||||
write_buffer = memoryview(self.palette.astype(np.uint32))
|
||||
fptr.write(write_buffer)
|
||||
else:
|
||||
# Not all the components are the same. More general, but much rarer
|
||||
|
|
@ -1920,13 +1922,11 @@ class PaletteBox(Jp2kBox):
|
|||
-------
|
||||
PaletteBox instance
|
||||
"""
|
||||
# Get the size of the palette.
|
||||
read_buffer = fptr.read(3)
|
||||
(num_entries, num_columns) = struct.unpack('>HB', read_buffer)
|
||||
(nrows, ncols) = struct.unpack('>HB', read_buffer)
|
||||
|
||||
# Need to determine bps and signed or not
|
||||
read_buffer = fptr.read(num_columns)
|
||||
bps_signed = struct.unpack('>' + 'B' * num_columns, read_buffer)
|
||||
read_buffer = fptr.read(ncols)
|
||||
bps_signed = struct.unpack('>' + 'B' * ncols, read_buffer)
|
||||
bps = [((x & 0x7f) + 1) for x in bps_signed]
|
||||
signed = [((x & 0x80) > 1) for x in bps_signed]
|
||||
|
||||
|
|
@ -1934,18 +1934,18 @@ class PaletteBox(Jp2kBox):
|
|||
# Ok the palette has the same datatype for all columns. We should
|
||||
# be able to efficiently read it.
|
||||
if bps[0] <= 8:
|
||||
nbytes_per_row = num_columns
|
||||
nbytes_per_row = ncols
|
||||
dtype = np.uint8
|
||||
elif bps[0] <= 16:
|
||||
nbytes_per_row = 2 * num_columns
|
||||
nbytes_per_row = 2 * ncols
|
||||
dtype = np.uint16
|
||||
elif bps[0] <= 32:
|
||||
nbytes_per_row = 3 * num_columns
|
||||
nbytes_per_row = 3 * ncols
|
||||
dtype = np.uint32
|
||||
|
||||
read_buffer = fptr.read(num_entries * nbytes_per_row)
|
||||
|
||||
read_buffer = fptr.read(nrows * nbytes_per_row)
|
||||
palette = np.frombuffer(read_buffer, dtype=dtype)
|
||||
palette = np.reshape(palette, (num_entries, num_columns))
|
||||
palette = np.reshape(palette, (nrows, ncols))
|
||||
|
||||
else:
|
||||
# General case where the columns may not be the same width.
|
||||
|
|
@ -1962,9 +1962,9 @@ class PaletteBox(Jp2kBox):
|
|||
# That means a list comprehension does this in one shot.
|
||||
row_nbytes = sum([int(math.ceil(x/8.0)) for x in bps])
|
||||
|
||||
read_buffer = fptr.read(num_entries * row_nbytes)
|
||||
palette = np.zeros((num_entries, num_columns), dtype=np.int32)
|
||||
for j in range(num_entries):
|
||||
read_buffer = fptr.read(nrows * row_nbytes)
|
||||
palette = np.zeros((nrows, ncols), dtype=np.int32)
|
||||
for j in range(nrows):
|
||||
palette[j] = struct.unpack_from(fmt, read_buffer,
|
||||
offset=j * row_nbytes)
|
||||
|
||||
|
|
@ -2828,13 +2828,15 @@ class UUIDListBox(Jp2kBox):
|
|||
-------
|
||||
UUIDListBox instance
|
||||
"""
|
||||
read_buffer = fptr.read(2)
|
||||
num_uuids, = struct.unpack('>H', read_buffer)
|
||||
num_bytes = offset + length - fptr.tell()
|
||||
read_buffer = fptr.read(num_bytes)
|
||||
|
||||
num_uuids, = struct.unpack_from('>H', read_buffer)
|
||||
|
||||
ulst = []
|
||||
for _ in range(num_uuids):
|
||||
read_buffer = fptr.read(16)
|
||||
ulst.append(uuid.UUID(bytes=read_buffer))
|
||||
for j in range(num_uuids):
|
||||
uuid_buffer = read_buffer[2 + j * 16 : 2 + (j + 1) * 16]
|
||||
ulst.append(uuid.UUID(bytes=uuid_buffer))
|
||||
|
||||
return cls(ulst, length=length, offset=offset)
|
||||
|
||||
|
|
|
|||
|
|
@ -691,7 +691,7 @@ class Jp2k(Jp2kBox):
|
|||
msg = "Unable to locate the specified codestream."
|
||||
raise IOError(msg)
|
||||
if L == 0:
|
||||
# The length of the box is presumed to last until the end of
|
||||
# The length of the box is presumed to last until the end of
|
||||
# the file. Compute the effective length of the box.
|
||||
L = os.path.getsize(ifile.name) - ifile.tell() + 8
|
||||
|
||||
|
|
@ -833,38 +833,12 @@ class Jp2k(Jp2kBox):
|
|||
"""
|
||||
self._subsampling_sanity_check()
|
||||
|
||||
# Must check the specified rlevel against the maximum.
|
||||
if rlevel != 0:
|
||||
# Must check the specified rlevel against the maximum.
|
||||
codestream = self.get_codestream()
|
||||
max_rlevel = codestream.segment[2].spcod[4]
|
||||
if rlevel == -1:
|
||||
# -1 is shorthand for the largest rlevel
|
||||
rlevel = max_rlevel
|
||||
elif rlevel < -1 or rlevel > max_rlevel:
|
||||
msg = "rlevel must be in the range [-1, {0}] for this image."
|
||||
msg = msg.format(max_rlevel)
|
||||
raise IOError(msg)
|
||||
dparameters = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef)
|
||||
|
||||
with ExitStack() as stack:
|
||||
try:
|
||||
# Set decoding parameters.
|
||||
# TODO: look to refactor, use _populate_dparam
|
||||
dparameters = opj.DecompressionParametersType()
|
||||
opj.set_default_decoder_parameters(ctypes.byref(dparameters))
|
||||
|
||||
if ignore_pclr_cmap_cdef is True:
|
||||
# Return raw codestream components.
|
||||
dparameters.flags |= 1
|
||||
|
||||
dparameters.cp_reduce = rlevel
|
||||
dparameters.decod_format = self._codec_format
|
||||
|
||||
infile = self.filename.encode()
|
||||
nelts = opj.PATH_LEN - len(infile)
|
||||
infile += b'0' * nelts
|
||||
dparameters.infile = infile
|
||||
|
||||
dinfo = opj.create_decompress(dparameters.decod_format)
|
||||
|
||||
event_mgr = opj.EventMgrType()
|
||||
|
|
@ -931,8 +905,8 @@ class Jp2k(Jp2kBox):
|
|||
"""
|
||||
self._subsampling_sanity_check()
|
||||
|
||||
dparam = self._populate_dparam(layer, rlevel, area, tile,
|
||||
ignore_pclr_cmap_cdef)
|
||||
dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef,
|
||||
layer=layer, tile=tile, area=area)
|
||||
|
||||
with ExitStack() as stack:
|
||||
if hasattr(opj2.OPENJP2,
|
||||
|
|
@ -976,8 +950,8 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
return img_array
|
||||
|
||||
def _populate_dparam(self, layer, rlevel, area, tile,
|
||||
ignore_pclr_cmap_cdef):
|
||||
def _populate_dparam(self, rlevel, ignore_pclr_cmap_cdef, tile=None,
|
||||
layer=None, area=None):
|
||||
"""Populate decompression structure with appropriate input parameters.
|
||||
|
||||
Parameters
|
||||
|
|
@ -1000,7 +974,11 @@ class Jp2k(Jp2kBox):
|
|||
dparam : DecompressionParametersType (ctypes)
|
||||
Corresponds to openjp2 decompression parameters structure.
|
||||
"""
|
||||
dparam = opj2.set_default_decoder_parameters()
|
||||
if opj2.OPENJP2 is not None:
|
||||
dparam = opj2.set_default_decoder_parameters()
|
||||
else:
|
||||
dparam = opj.DecompressionParametersType()
|
||||
opj.set_default_decoder_parameters(ctypes.byref(dparam))
|
||||
|
||||
infile = self.filename.encode()
|
||||
nelts = opj2.PATH_LEN - len(infile)
|
||||
|
|
@ -1009,12 +987,22 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
dparam.decod_format = self._codec_format
|
||||
|
||||
dparam.cp_layer = layer
|
||||
if layer is not None:
|
||||
dparam.cp_layer = layer
|
||||
|
||||
if rlevel == -1:
|
||||
# Get the lowest resolution thumbnail.
|
||||
# Must check the specified rlevel against the maximum.
|
||||
if rlevel != 0:
|
||||
# Must check the specified rlevel against the maximum.
|
||||
codestream = self.get_codestream()
|
||||
rlevel = codestream.segment[2].spcod[4]
|
||||
max_rlevel = codestream.segment[2].spcod[4]
|
||||
if rlevel == -1:
|
||||
# -1 is shorthand for the largest rlevel
|
||||
rlevel = max_rlevel
|
||||
elif rlevel < -1 or rlevel > max_rlevel:
|
||||
msg = "rlevel must be in the range [-1, {0}] for this image."
|
||||
msg = msg.format(max_rlevel)
|
||||
raise IOError(msg)
|
||||
|
||||
dparam.cp_reduce = rlevel
|
||||
|
||||
if area is not None:
|
||||
|
|
@ -1088,7 +1076,8 @@ class Jp2k(Jp2kBox):
|
|||
"of OpenJP2 installed before using "
|
||||
"this functionality.")
|
||||
|
||||
dparam = self._populate_dparam(layer, rlevel, area, tile, ignore_pclr_cmap_cdef)
|
||||
dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef,
|
||||
layer=layer, tile=tile, area=area)
|
||||
|
||||
with ExitStack() as stack:
|
||||
if hasattr(opj2.OPENJP2,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue