pylint work, #99
This commit is contained in:
parent
77a601b4de
commit
bf24e9b280
2 changed files with 417 additions and 280 deletions
|
|
@ -1234,13 +1234,13 @@ class PaletteBox(Jp2kBox):
|
|||
|
||||
# Need to determine bps and signed or not
|
||||
read_buffer = fptr.read(num_columns)
|
||||
#data = struct.unpack('>' + 'B' * num_columns, read_buffer)
|
||||
bps = [((x & 0x07f) + 1) for x in read_buffer]
|
||||
signed = [((x & 0x80) > 1) for x in read_buffer]
|
||||
data = struct.unpack('>' + 'B' * num_columns, read_buffer)
|
||||
bps = [((x & 0x07f) + 1) for x in data]
|
||||
signed = [((x & 0x80) > 1) for x in data]
|
||||
|
||||
# Each palette component is padded out to the next largest byte.
|
||||
# That means a list comprehension does this in one shot.
|
||||
row_nbytes = sum([math.ceil(x/8) for x in bps])
|
||||
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
|
||||
|
|
|
|||
689
glymur/jp2k.py
689
glymur/jp2k.py
|
|
@ -187,36 +187,147 @@ class Jp2k(Jp2kBox):
|
|||
msg += "profile if the file type box brand is 'jp2 '."
|
||||
warnings.warn(msg)
|
||||
|
||||
def _validate_write_parameters(self, img_array, code_block_size,
|
||||
precinct_sizes, cratios, psnr, colorspace,
|
||||
codec_fmt):
|
||||
"""Check that the input parameters to the write function are valid.
|
||||
#def _populate_cparams(self, cbsize, cratios, eph, grid_offset, modesw,
|
||||
# numres, prog, psnr, psizes, sop, subsam, tilesize):
|
||||
def _populate_cparams(self, **kwargs):
|
||||
"""Populate compression parameters structure from input arguments.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cbsize : tuple, optional
|
||||
Code block size (DY, DX).
|
||||
cratios : iterable
|
||||
Compression ratios for successive layers.
|
||||
eph : bool, optional
|
||||
If true, write SOP marker after each header packet.
|
||||
grid_offset : tuple, optional
|
||||
Offset (DY, DX) of the origin of the image in the reference grid.
|
||||
mct : bool, optional
|
||||
Specifies usage of the multi component transform. If not
|
||||
specified, defaults to True if the colorspace is RGB.
|
||||
modesw : int, optional
|
||||
Mode switch.
|
||||
1 = BYPASS(LAZY)
|
||||
2 = RESET
|
||||
4 = RESTART(TERMALL)
|
||||
8 = VSC
|
||||
16 = ERTERM(SEGTERM)
|
||||
32 = SEGMARK(SEGSYM)
|
||||
numres : int, optional
|
||||
Number of resolutions.
|
||||
prog : str, optional
|
||||
Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL".
|
||||
psnr : iterable, optional
|
||||
Different PSNR for successive layers.
|
||||
psizes : list, optional
|
||||
List of precinct sizes. Each precinct size tuple is defined in
|
||||
(height x width).
|
||||
sop : bool, optional
|
||||
If true, write SOP marker before each packet.
|
||||
subsam : tuple, optional
|
||||
Subsampling factors (dy, dx).
|
||||
tilesize : tuple, optional
|
||||
Numeric tuple specifying tile size in terms of (numrows, numcols),
|
||||
not (X, Y).
|
||||
|
||||
Returns
|
||||
-------
|
||||
cparams : CompressionParametersType(ctypes.Structure)
|
||||
Corresponds to cparameters_t type in openjp2 headers.
|
||||
"""
|
||||
|
||||
cparams = _opj2.set_default_encoder_parameters()
|
||||
|
||||
outfile = self.filename.encode()
|
||||
num_pad_bytes = _opj2.PATH_LEN - len(outfile)
|
||||
outfile += b'0' * num_pad_bytes
|
||||
cparams.outfile = outfile
|
||||
|
||||
if self.filename[-4:].lower() == '.jp2':
|
||||
cparams.codec_fmt = _opj2.CODEC_JP2
|
||||
else:
|
||||
cparams.codec_fmt = _opj2.CODEC_J2K
|
||||
|
||||
# Set defaults to lossless to begin.
|
||||
cparams.tcp_rates[0] = 0
|
||||
cparams.tcp_numlayers = 1
|
||||
cparams.cp_disto_alloc = 1
|
||||
|
||||
if 'cbsize' in kwargs:
|
||||
cparams.cblockw_init = kwargs['cbsize'][1]
|
||||
cparams.cblockh_init = kwargs['cbsize'][0]
|
||||
|
||||
if 'cratios' in kwargs:
|
||||
cparams.tcp_numlayers = len(kwargs['cratios'])
|
||||
for j, cratio in enumerate(kwargs['cratios']):
|
||||
cparams.tcp_rates[j] = cratio
|
||||
cparams.cp_disto_alloc = 1
|
||||
|
||||
if 'eph' in kwargs:
|
||||
cparams.csty |= 0x04
|
||||
|
||||
if 'grid_offset' in kwargs:
|
||||
cparams.image_offset_x0 = kwargs['grid_offset'][1]
|
||||
cparams.image_offset_y0 = kwargs['grid_offset'][0]
|
||||
|
||||
if 'modesw' in kwargs:
|
||||
for shift in range(6):
|
||||
power_of_two = 1 << shift
|
||||
if kwargs['modesw'] & power_of_two:
|
||||
cparams.mode |= power_of_two
|
||||
|
||||
if 'numres' in kwargs:
|
||||
cparams.numresolution = kwargs['numres']
|
||||
|
||||
if 'prog' in kwargs:
|
||||
prog = kwargs['prog'].upper()
|
||||
cparams.prog_order = PROGRESSION_ORDER[prog]
|
||||
|
||||
if 'psnr' in kwargs:
|
||||
cparams.tcp_numlayers = len(kwargs['psnr'])
|
||||
for j, snr_layer in enumerate(kwargs['psnr']):
|
||||
cparams.tcp_distoratio[j] = snr_layer
|
||||
cparams.cp_fixed_quality = 1
|
||||
|
||||
if 'psizes' in kwargs:
|
||||
for j, (prch, prcw) in enumerate(kwargs['psizes']):
|
||||
cparams.prcw_init[j] = prcw
|
||||
cparams.prch_init[j] = prch
|
||||
cparams.csty |= 0x01
|
||||
cparams.res_spec = len(kwargs['psizes'])
|
||||
|
||||
if 'sop' in kwargs:
|
||||
cparams.csty |= 0x02
|
||||
|
||||
if 'subsam' in kwargs:
|
||||
cparams.subsampling_dy = kwargs['subsam'][0]
|
||||
cparams.subsampling_dx = kwargs['subsam'][1]
|
||||
|
||||
if 'tilesize' in kwargs:
|
||||
cparams.cp_tdx = kwargs['tilesize'][1]
|
||||
cparams.cp_tdy = kwargs['tilesize'][0]
|
||||
cparams.tile_size_on = _opj2.TRUE
|
||||
|
||||
return cparams
|
||||
|
||||
def _validate_compression_params(self, img_array, cparams):
|
||||
"""Check that the compression parameters are valid.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img_array : ndarray
|
||||
Image data to be written to file.
|
||||
code_block_size : tuple
|
||||
Code block size (DY, DX).
|
||||
precinct_sizes : list
|
||||
List of precinct sizes. Each precinct size tuple is defined in
|
||||
(height x width).
|
||||
cratios : iterable
|
||||
Compression ratios for successive layers.
|
||||
psnr : iterable
|
||||
Different PSNR for successive layers.
|
||||
mct : bool
|
||||
Specifies usage of the multi component transform. If not
|
||||
specified, defaults to True if the colorspace is RGB.
|
||||
colorspace : str, optional
|
||||
Either 'rgb' or 'gray'.
|
||||
codec_fmt : int
|
||||
Are we writing a JP2 file or a J2K file?
|
||||
cparams : CompressionParametersType(ctypes.Structure)
|
||||
Corresponds to cparameters_t type in openjp2 headers.
|
||||
"""
|
||||
# Validate code block size and precinct sizes.
|
||||
if code_block_size is not None:
|
||||
width = code_block_size[1]
|
||||
height = code_block_size[0]
|
||||
|
||||
# Code block size
|
||||
code_block_specified = False
|
||||
if cparams.cblockw_init != 0 and cparams.cblockh_init != 0:
|
||||
# These fields ARE zero if uninitialized.
|
||||
width = cparams.cblockw_init
|
||||
height = cparams.cblockh_init
|
||||
code_block_specified = True
|
||||
if height * width > 4096 or height < 4 or width < 4:
|
||||
msg = "Code block area cannot exceed 4096. "
|
||||
msg += "Code block height and width must be larger than 4."
|
||||
|
|
@ -226,12 +337,16 @@ class Jp2k(Jp2kBox):
|
|||
msg = "Bad code block size ({0}, {1}), "
|
||||
msg += "must be powers of 2."
|
||||
raise IOError(msg.format(height, width))
|
||||
|
||||
if precinct_sizes is not None:
|
||||
for j, (prch, prcw) in enumerate(precinct_sizes):
|
||||
if j == 0 and code_block_size is not None:
|
||||
cblkh, cblkw = code_block_size
|
||||
if cblkh * 2 > prch or cblkw * 2 > prcw:
|
||||
|
||||
# Precinct size
|
||||
if cparams.res_spec != 0:
|
||||
# precinct size was not specified if this field is zero.
|
||||
for j in range(cparams.res_spec):
|
||||
prch = cparams.prch_init[j]
|
||||
prcw = cparams.prcw_init[j]
|
||||
if j == 0 and code_block_specified:
|
||||
height, width = cparams.cblockh_init, cparams.cblockw_init
|
||||
if height * 2 > prch or width * 2 > prcw:
|
||||
msg = "Highest Resolution precinct size must be at "
|
||||
msg += "least twice that of the code block dimensions."
|
||||
raise IOError(msg)
|
||||
|
|
@ -240,16 +355,12 @@ class Jp2k(Jp2kBox):
|
|||
msg = "Bad precinct sizes ({0}, {1}), "
|
||||
msg += "must be powers of 2."
|
||||
raise IOError(msg.format(prch, prcw))
|
||||
|
||||
if cratios is not None and psnr is not None:
|
||||
msg = "Cannot specify cratios and psnr together."
|
||||
raise IOError(msg)
|
||||
|
||||
|
||||
# What would the point of 1D images be?
|
||||
if img_array.ndim == 1 or img_array.ndim > 3:
|
||||
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
||||
raise IOError(msg)
|
||||
|
||||
|
||||
if _OPENJP2_IS_OFFICIAL_V2:
|
||||
if (((img_array.ndim != 2) and
|
||||
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
||||
|
|
@ -258,28 +369,110 @@ class Jp2k(Jp2kBox):
|
|||
msg += "the OpenJPEG library version is the official 2.0.0 "
|
||||
msg += "release."
|
||||
raise IOError(msg)
|
||||
|
||||
if colorspace is not None:
|
||||
if codec_fmt == _opj2.CODEC_J2K:
|
||||
msg = 'Do not specify a colorspace when writing a raw '
|
||||
msg += 'codestream.'
|
||||
raise IOError(msg)
|
||||
if colorspace.lower() not in ('rgb', 'grey', 'gray'):
|
||||
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
||||
raise IOError(msg)
|
||||
elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3:
|
||||
msg = 'RGB colorspace requires at least 3 components.'
|
||||
raise IOError(msg)
|
||||
|
||||
|
||||
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
|
||||
msg = "Only uint8 and uint16 images are currently supported."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
def _set_multi_component_transform(self, colorspace, cparams, mct=None):
|
||||
"""Set multi component transform usage.
|
||||
|
||||
# pylint: disable-msg=W0221
|
||||
def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None,
|
||||
cbsize=None, psizes=None, grid_offset=None, sop=False,
|
||||
subsam=None, tilesize=None, prog=None, modesw=None,
|
||||
colorspace=None, verbose=False, mct=None):
|
||||
Parameters
|
||||
----------
|
||||
colorspace : int
|
||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
||||
cparams : CompressionParametersType(ctypes.Structure)
|
||||
Corresponds to cparameters_t type in openjp2 headers.
|
||||
mct : bool, optional
|
||||
Specifies usage of the multi component transform. If not
|
||||
specified, defaults to True if the colorspace is RGB.
|
||||
"""
|
||||
if mct is None:
|
||||
# If the multi component transform was not specified, we infer
|
||||
# that it should be used if the color space is RGB.
|
||||
if colorspace == _opj2.CLRSPC_SRGB:
|
||||
cparams.tcp_mct = 1
|
||||
else:
|
||||
cparams.tcp_mct = 0
|
||||
else:
|
||||
# MCT was specified. Does it make sense?
|
||||
if mct and colorspace == _opj2.CLRSPC_GRAY:
|
||||
# Cannot check for this in the validate routine, as we need
|
||||
# to know what the target colorspace has been determined to be.
|
||||
msg = "Cannot specify usage of the multi component transform "
|
||||
msg += "if the colorspace is gray."
|
||||
raise IOError(msg)
|
||||
cparams.tcp_mct = 1 if mct else 0
|
||||
|
||||
def _process_write_inputs(self, img_array, colorspace=None, **kwargs):
|
||||
"""Directs processing of write method arguments.
|
||||
|
||||
It's somewhat awkward to process all the kwargs arguments at once.
|
||||
The "colorspace" is not a parameter that gets processed into the
|
||||
compression parameters structure, and it unfortunately must be handled
|
||||
in the middle of the compression parameter processing.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
Returns
|
||||
-------
|
||||
cparams : CompressionParametersType(ctypes.Structure)
|
||||
Corresponds to cparameters_t type in openjp2 headers.
|
||||
colorspace : int
|
||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
||||
"""
|
||||
|
||||
if 'cratios' in kwargs and 'psnr' in kwargs:
|
||||
msg = "Cannot specify cratios and psnr together."
|
||||
raise IOError(msg)
|
||||
|
||||
cparams = self._populate_cparams(**kwargs)
|
||||
self._validate_compression_params(img_array, cparams)
|
||||
|
||||
colorspace = _unpack_colorspace(colorspace, img_array, cparams)
|
||||
|
||||
try:
|
||||
mct = kwargs['mct']
|
||||
except KeyError:
|
||||
mct = None
|
||||
self._set_multi_component_transform(colorspace, cparams, mct)
|
||||
|
||||
return cparams, colorspace
|
||||
|
||||
def _populate_image_struct(self, cparams, image, imgdata):
|
||||
"""Populates image struct needed for compression.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cparams : CompressionParametersType(ctypes.Structure)
|
||||
Corresponds to cparameters_t type in openjp2 headers.
|
||||
image : ImageType(ctypes.Structure)
|
||||
Corresponds to image_t type in openjp2 headers.
|
||||
imgarray : ndarray
|
||||
Image data to be written to file.
|
||||
"""
|
||||
|
||||
numrows, numcols, num_comps = imgdata.shape
|
||||
|
||||
# set image offset and reference grid
|
||||
image.contents.x0 = cparams.image_offset_x0
|
||||
image.contents.y0 = cparams.image_offset_y0
|
||||
image.contents.x1 = (image.contents.x0 +
|
||||
(numcols - 1) * cparams.subsampling_dx + 1)
|
||||
image.contents.y1 = (image.contents.y0 +
|
||||
(numrows - 1) * cparams.subsampling_dy + 1)
|
||||
|
||||
# Stage the image data to the openjpeg data structure.
|
||||
for k in range(0, num_comps):
|
||||
layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32)
|
||||
dest = image.contents.comps[k].data
|
||||
src = layer.ctypes.data
|
||||
ctypes.memmove(dest, src, layer.nbytes)
|
||||
|
||||
return image
|
||||
|
||||
def write(self, img_array, verbose=False, **kwargs):
|
||||
"""Write image data to a JP2/JPX/J2k file. Intended usage of the
|
||||
various parameters follows that of OpenJPEG's opj_compress utility.
|
||||
|
||||
|
|
@ -290,11 +483,6 @@ class Jp2k(Jp2kBox):
|
|||
----------
|
||||
img_array : ndarray
|
||||
Image data to be written to file.
|
||||
callbacks : bool, optional
|
||||
If true, enable default info handler such that INFO messages
|
||||
produced by the OpenJPEG library are output to the console. By
|
||||
default, OpenJPEG warning and error messages are captured by
|
||||
Python's own warning and error mechanisms.
|
||||
cbsize : tuple, optional
|
||||
Code block size (DY, DX).
|
||||
colorspace : str, optional
|
||||
|
|
@ -356,128 +544,17 @@ class Jp2k(Jp2kBox):
|
|||
"installed before using this "
|
||||
"functionality.")
|
||||
|
||||
if self.filename[-4:].lower() == '.jp2':
|
||||
codec_fmt = _opj2.CODEC_JP2
|
||||
else:
|
||||
codec_fmt = _opj2.CODEC_J2K
|
||||
|
||||
self._validate_write_parameters(img_array, cbsize, psizes, cratios,
|
||||
psnr, colorspace, codec_fmt)
|
||||
|
||||
cparams = _opj2.set_default_encoder_parameters()
|
||||
|
||||
outfile = self.filename.encode()
|
||||
num_pad_bytes = _opj2.PATH_LEN - len(outfile)
|
||||
outfile += b'0' * num_pad_bytes
|
||||
cparams.outfile = outfile
|
||||
|
||||
cparams.cod_format = codec_fmt
|
||||
|
||||
# Set defaults to lossless to begin.
|
||||
cparams.tcp_rates[0] = 0
|
||||
cparams.tcp_numlayers = 1
|
||||
cparams.cp_disto_alloc = 1
|
||||
|
||||
if cbsize is not None:
|
||||
width = cbsize[1]
|
||||
height = cbsize[0]
|
||||
cparams.cblockw_init = width
|
||||
cparams.cblockh_init = height
|
||||
|
||||
if cratios is not None:
|
||||
cparams.tcp_numlayers = len(cratios)
|
||||
for j, cratio in enumerate(cratios):
|
||||
cparams.tcp_rates[j] = cratio
|
||||
cparams.cp_disto_alloc = 1
|
||||
|
||||
if eph:
|
||||
cparams.csty |= 0x04
|
||||
|
||||
if grid_offset is not None:
|
||||
cparams.image_offset_x0 = grid_offset[1]
|
||||
cparams.image_offset_y0 = grid_offset[0]
|
||||
|
||||
if modesw is not None:
|
||||
for shift in range(6):
|
||||
power_of_two = 1 << shift
|
||||
if modesw & power_of_two:
|
||||
cparams.mode |= power_of_two
|
||||
|
||||
if numres is not None:
|
||||
cparams.numresolution = numres
|
||||
|
||||
if prog is not None:
|
||||
prog = prog.upper()
|
||||
cparams.prog_order = PROGRESSION_ORDER[prog]
|
||||
|
||||
if psnr is not None:
|
||||
cparams.tcp_numlayers = len(psnr)
|
||||
for j, snr_layer in enumerate(psnr):
|
||||
cparams.tcp_distoratio[j] = snr_layer
|
||||
cparams.cp_fixed_quality = 1
|
||||
|
||||
if psizes is not None:
|
||||
for j, (prch, prcw) in enumerate(psizes):
|
||||
cparams.prcw_init[j] = prcw
|
||||
cparams.prch_init[j] = prch
|
||||
cparams.csty |= 0x01
|
||||
cparams.res_spec = len(psizes)
|
||||
|
||||
if sop:
|
||||
cparams.csty |= 0x02
|
||||
|
||||
if subsam is not None:
|
||||
cparams.subsampling_dy = subsam[0]
|
||||
cparams.subsampling_dx = subsam[1]
|
||||
|
||||
if tilesize is not None:
|
||||
cparams.cp_tdx = tilesize[1]
|
||||
cparams.cp_tdy = tilesize[0]
|
||||
cparams.tile_size_on = _opj2.TRUE
|
||||
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
||||
|
||||
if img_array.ndim == 2:
|
||||
# Force it to be 3D. Just makes things easier later on.
|
||||
# Force the image to be 3D. Just makes things easier later on.
|
||||
numrows, numcols = img_array.shape
|
||||
img_array = img_array.reshape(numrows, numcols, 1)
|
||||
|
||||
# Only two precisions are possible.
|
||||
comp_prec = 8 if img_array.dtype == np.uint8 else 16
|
||||
|
||||
numrows, numcols, num_comps = img_array.shape
|
||||
|
||||
if colorspace is None:
|
||||
# Must infer the colorspace from the image dimensions.
|
||||
if img_array.shape[2] == 1 or img_array.shape[2] == 2:
|
||||
# A single channel image or an image with two channels is going
|
||||
# to be greyscale.
|
||||
colorspace = _opj2.CLRSPC_GRAY
|
||||
else:
|
||||
# Anything else must be RGB, right?
|
||||
colorspace = _opj2.CLRSPC_SRGB
|
||||
else:
|
||||
# Turn the colorspace from a string to the enumerated value that
|
||||
# the library expects.
|
||||
colorspace = _COLORSPACE_MAP[colorspace.lower()]
|
||||
|
||||
if mct is None:
|
||||
# If the multi component transform was not specified, we infer
|
||||
# that it should be used if the color space is RGB.
|
||||
if colorspace == _opj2.CLRSPC_SRGB:
|
||||
cparams.tcp_mct = 1
|
||||
else:
|
||||
cparams.tcp_mct = 0
|
||||
else:
|
||||
if mct and colorspace == _opj2.CLRSPC_GRAY:
|
||||
# Cannot check for this in the validate routine, as we need
|
||||
# to know what the target colorspace has been determined to be.
|
||||
msg = "Cannot specify usage of the multi component transform "
|
||||
msg += "if the colorspace is gray."
|
||||
raise IOError(msg)
|
||||
cparams.tcp_mct = 1 if mct else 0
|
||||
|
||||
if img_array.dtype == np.uint8:
|
||||
comp_prec = 8
|
||||
else:
|
||||
# We already know it cannot be anything else than uint16.
|
||||
comp_prec = 16
|
||||
|
||||
comptparms = (_opj2.ImageComptParmType * num_comps)()
|
||||
for j in range(num_comps):
|
||||
comptparms[j].dx = cparams.subsampling_dx
|
||||
|
|
@ -491,37 +568,22 @@ class Jp2k(Jp2kBox):
|
|||
comptparms[j].sgnd = 0
|
||||
|
||||
image = _opj2.image_create(comptparms, colorspace)
|
||||
self._populate_image_struct(cparams, image, img_array)
|
||||
|
||||
# set image offset and reference grid
|
||||
image.contents.x0 = cparams.image_offset_x0
|
||||
image.contents.y0 = cparams.image_offset_y0
|
||||
image.contents.x1 = (image.contents.x0 +
|
||||
(numcols - 1) * cparams.subsampling_dx + 1)
|
||||
image.contents.y1 = (image.contents.y0 +
|
||||
(numrows - 1) * cparams.subsampling_dy + 1)
|
||||
|
||||
# Stage the image data to the openjpeg data structure.
|
||||
for k in range(0, num_comps):
|
||||
layer = np.ascontiguousarray(img_array[:, :, k], dtype=np.int32)
|
||||
dest = image.contents.comps[k].data
|
||||
src = layer.ctypes.data
|
||||
ctypes.memmove(dest, src, layer.nbytes)
|
||||
|
||||
codec = _opj2.create_compress(codec_fmt)
|
||||
|
||||
if verbose:
|
||||
_opj2.set_info_handler(codec, _INFO_CALLBACK)
|
||||
else:
|
||||
_opj2.set_info_handler(codec, None)
|
||||
codec = _opj2.create_compress(cparams.codec_fmt)
|
||||
|
||||
info_handler = _INFO_CALLBACK if verbose else None
|
||||
_opj2.set_info_handler(codec, info_handler)
|
||||
_opj2.set_warning_handler(codec, _WARNING_CALLBACK)
|
||||
_opj2.set_error_handler(codec, _ERROR_CALLBACK)
|
||||
|
||||
_opj2.setup_encoder(codec, cparams, image)
|
||||
|
||||
if _OPENJP2_IS_OFFICIAL_V2:
|
||||
fptr = _libc.fopen(self.filename, 'wb')
|
||||
strm = _opj2.stream_create_default_file_stream(fptr, False)
|
||||
else:
|
||||
# This routine introduced in 2.0 devel series.
|
||||
strm = _opj2.stream_create_default_file_stream_v3(self.filename,
|
||||
False)
|
||||
|
||||
|
|
@ -534,6 +596,7 @@ class Jp2k(Jp2kBox):
|
|||
_opj2.stream_destroy(strm)
|
||||
_libc.fclose(fptr)
|
||||
else:
|
||||
# This routine introduced in 2.0 devel series.
|
||||
_opj2.stream_destroy_v3(strm)
|
||||
|
||||
_opj2.destroy_codec(codec)
|
||||
|
|
@ -745,32 +808,11 @@ class Jp2k(Jp2kBox):
|
|||
stack.callback(_opj.destroy_decompress, dinfo)
|
||||
stack.callback(_opj.cio_close, cio)
|
||||
|
||||
ncomps = image.contents.numcomps
|
||||
component = image.contents.comps[0]
|
||||
dtype = component2dtype(component)
|
||||
|
||||
nrows = image.contents.comps[0].h
|
||||
ncols = image.contents.comps[0].w
|
||||
ncomps = image.contents.numcomps
|
||||
data = np.zeros((nrows, ncols, ncomps), dtype)
|
||||
|
||||
for k in range(image.contents.numcomps):
|
||||
component = image.contents.comps[k]
|
||||
nrows = component.h
|
||||
ncols = component.w
|
||||
|
||||
_validate_nonzero_image_size(nrows, ncols, k)
|
||||
|
||||
addr = ctypes.addressof(component.data.contents)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
nelts = nrows * ncols
|
||||
band = np.ctypeslib.as_array(
|
||||
(ctypes.c_int32 * nelts).from_address(addr))
|
||||
data[:, :, k] = np.reshape(band.astype(dtype),
|
||||
(nrows, ncols))
|
||||
data = extract_image_cube(image)
|
||||
|
||||
if data.shape[2] == 1:
|
||||
# The third dimension has just a single layer. Make the image
|
||||
# data 2D instead of 3D.
|
||||
data = data.view()
|
||||
data.shape = data.shape[0:2]
|
||||
|
||||
|
|
@ -820,6 +862,59 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
return img_array
|
||||
|
||||
def _populate_dparam(self, layer, rlevel, area, tile):
|
||||
"""Populate decompression structure with appropriate input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
layer : int, optional
|
||||
Number of quality layer to decode.
|
||||
rlevel : int, optional
|
||||
Factor by which to rlevel output resolution.
|
||||
area : tuple, optional
|
||||
Specifies decoding image area,
|
||||
(first_row, first_col, last_row, last_col)
|
||||
tile : int, optional
|
||||
Number of tile to decode.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dparam : DecompressionParametersType (ctypes)
|
||||
Corresponds to openjp2 decompression parameters structure.
|
||||
"""
|
||||
dparam = _opj2.set_default_decoder_parameters()
|
||||
|
||||
infile = self.filename.encode()
|
||||
nelts = _opj2.PATH_LEN - len(infile)
|
||||
infile += b'0' * nelts
|
||||
dparam.infile = infile
|
||||
|
||||
dparam.decod_format = self._codec_format
|
||||
|
||||
dparam.cp_layer = layer
|
||||
|
||||
if rlevel == -1:
|
||||
# Get the lowest resolution thumbnail.
|
||||
codestream = self.get_codestream()
|
||||
rlevel = codestream.segment[2].spcod[4]
|
||||
dparam.cp_reduce = rlevel
|
||||
|
||||
if area is not None:
|
||||
if area[0] < 0 or area[1] < 0 or area[2] <= 0 or area[3] <= 0:
|
||||
msg = "Upper left corner coordinates must be nonnegative and "
|
||||
msg += "lower right corner coordinates must be positive: {0}"
|
||||
raise IOError(msg.format(area))
|
||||
dparam.DA_y0 = area[0]
|
||||
dparam.DA_x0 = area[1]
|
||||
dparam.DA_y1 = area[2]
|
||||
dparam.DA_x1 = area[3]
|
||||
|
||||
if tile is not None:
|
||||
dparam.tile_index = tile
|
||||
dparam.nb_tile_to_decode = 1
|
||||
|
||||
return dparam
|
||||
|
||||
def _read_common(self, rlevel=0, layer=0, area=None, tile=None,
|
||||
verbose=False, as_bands=False):
|
||||
"""Read a JPEG 2000 image.
|
||||
|
|
@ -845,36 +940,7 @@ class Jp2k(Jp2kBox):
|
|||
img_array : ndarray
|
||||
The individual image components or a single array.
|
||||
"""
|
||||
dparam = _opj2.set_default_decoder_parameters()
|
||||
|
||||
infile = self.filename.encode()
|
||||
nelts = _opj2.PATH_LEN - len(infile)
|
||||
infile += b'0' * nelts
|
||||
dparam.infile = infile
|
||||
|
||||
dparam.decod_format = self._codec_format
|
||||
|
||||
dparam.cp_layer = layer
|
||||
|
||||
if rlevel == -1:
|
||||
# Get the lowest resolution thumbnail.
|
||||
codestream = self.get_codestream()
|
||||
rlevel = codestream.segment[2].spcod[4]
|
||||
|
||||
dparam.cp_reduce = rlevel
|
||||
if area is not None:
|
||||
if area[0] < 0 or area[1] < 0 or area[2] <= 0 or area[3] <= 0:
|
||||
msg = "Upper left corner coordinates must be nonnegative and "
|
||||
msg += "lower right corner coordinates must be positive: {0}"
|
||||
raise IOError(msg.format(area))
|
||||
dparam.DA_y0 = area[0]
|
||||
dparam.DA_x0 = area[1]
|
||||
dparam.DA_y1 = area[2]
|
||||
dparam.DA_x1 = area[3]
|
||||
|
||||
if tile is not None:
|
||||
dparam.tile_index = tile
|
||||
dparam.nb_tile_to_decode = 1
|
||||
dparam = self._populate_dparam(layer, rlevel, area, tile)
|
||||
|
||||
with ExitStack() as stack:
|
||||
if hasattr(_opj2.OPENJP2,
|
||||
|
|
@ -911,34 +977,10 @@ class Jp2k(Jp2kBox):
|
|||
_opj2.decode(codec, stream, image)
|
||||
_opj2.end_decompress(codec, stream)
|
||||
|
||||
component = image.contents.comps[0]
|
||||
dtype = component2dtype(component)
|
||||
|
||||
if as_bands:
|
||||
data = []
|
||||
data = extract_image_bands(image)
|
||||
else:
|
||||
nrows = image.contents.comps[0].h
|
||||
ncols = image.contents.comps[0].w
|
||||
ncomps = image.contents.numcomps
|
||||
data = np.zeros((nrows, ncols, ncomps), dtype)
|
||||
|
||||
for k in range(image.contents.numcomps):
|
||||
component = image.contents.comps[k]
|
||||
nrows = component.h
|
||||
ncols = component.w
|
||||
|
||||
_validate_nonzero_image_size(nrows, ncols, k)
|
||||
|
||||
addr = ctypes.addressof(component.data.contents)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
band = np.ctypeslib.as_array(
|
||||
(ctypes.c_int32 * nrows * ncols).from_address(addr))
|
||||
if as_bands:
|
||||
data.append(np.reshape(band.astype(dtype), (nrows, ncols)))
|
||||
else:
|
||||
data[:, :, k] = np.reshape(band.astype(dtype),
|
||||
(nrows, ncols))
|
||||
data = extract_image_cube(image)
|
||||
|
||||
return data
|
||||
|
||||
|
|
@ -1170,3 +1212,98 @@ def _validate_jp2_box_sequence(boxes):
|
|||
msg += "channel definition box."
|
||||
raise IOError(msg)
|
||||
|
||||
def extract_image_cube(image):
|
||||
"""Extract 3D image from openjpeg data structure.
|
||||
"""
|
||||
ncomps = image.contents.numcomps
|
||||
component = image.contents.comps[0]
|
||||
dtype = component2dtype(component)
|
||||
|
||||
nrows = component.h
|
||||
ncols = component.w
|
||||
data = np.zeros((nrows, ncols, ncomps), dtype)
|
||||
|
||||
for k in range(image.contents.numcomps):
|
||||
component = image.contents.comps[k]
|
||||
nrows = component.h
|
||||
ncols = component.w
|
||||
|
||||
_validate_nonzero_image_size(nrows, ncols, k)
|
||||
|
||||
addr = ctypes.addressof(component.data.contents)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
nelts = nrows * ncols
|
||||
band = np.ctypeslib.as_array(
|
||||
(ctypes.c_int32 * nelts).from_address(addr))
|
||||
data[:, :, k] = np.reshape(band.astype(dtype), (nrows, ncols))
|
||||
|
||||
return data
|
||||
|
||||
def extract_image_bands(image):
|
||||
"""Extract unequally-sized image bands.
|
||||
|
||||
This routine need only be called when subsampling differs across image
|
||||
components, such as is often the case with YCbCr imagery.
|
||||
"""
|
||||
data = []
|
||||
for k in range(image.contents.numcomps):
|
||||
component = image.contents.comps[k]
|
||||
|
||||
dtype = component2dtype(component)
|
||||
nrows = component.h
|
||||
ncols = component.w
|
||||
|
||||
_validate_nonzero_image_size(nrows, ncols, k)
|
||||
|
||||
addr = ctypes.addressof(component.data.contents)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
band = np.ctypeslib.as_array(
|
||||
(ctypes.c_int32 * nrows * ncols).from_address(addr))
|
||||
data.append(np.reshape(band.astype(dtype), (nrows, ncols)))
|
||||
|
||||
return data
|
||||
|
||||
def _unpack_colorspace(colorspace, img_array, cparams):
|
||||
"""Determine the colorspace from the supplied inputs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
colorspace : int
|
||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
||||
img_array : ndarray
|
||||
Image data to be written to file.
|
||||
cparams : CompressionParametersType(ctypes.Structure)
|
||||
Corresponds to cparameters_t type in openjp2 headers.
|
||||
"""
|
||||
if colorspace is None:
|
||||
# Must infer the colorspace from the image dimensions.
|
||||
if img_array.ndim < 3:
|
||||
# A single channel image is grayscale.
|
||||
colorspace = _opj2.CLRSPC_GRAY
|
||||
elif img_array.shape[2] == 1 or img_array.shape[2] == 2:
|
||||
# A single channel image or an image with two channels is going
|
||||
# to be greyscale.
|
||||
colorspace = _opj2.CLRSPC_GRAY
|
||||
else:
|
||||
# Anything else must be RGB, right?
|
||||
colorspace = _opj2.CLRSPC_SRGB
|
||||
else:
|
||||
# Supplied a string colorspace, so we must validate it.
|
||||
if cparams.codec_fmt == _opj2.CODEC_J2K:
|
||||
msg = 'Do not specify a colorspace when writing a raw '
|
||||
msg += 'codestream.'
|
||||
raise IOError(msg)
|
||||
if colorspace.lower() not in ('rgb', 'grey', 'gray'):
|
||||
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
||||
raise IOError(msg)
|
||||
elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3:
|
||||
msg = 'RGB colorspace requires at least 3 components.'
|
||||
raise IOError(msg)
|
||||
|
||||
# Turn the colorspace from a string to the enumerated value that
|
||||
# the library expects.
|
||||
colorspace = _COLORSPACE_MAP[colorspace.lower()]
|
||||
|
||||
return colorspace
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue