reduce cyclomatic complexity where possible

This commit is contained in:
John Evans 2015-01-13 14:44:03 -05:00
commit cbdd6200b6

View file

@ -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,