Remove deprecated modules and functions from bioxel package
- Deleted _ni_support.py, _utils.py, and dtype.py as they contained unused or redundant functions. - Removed the resize function and related utilities from _warps.py to streamline image processing. - Cleaned up the skimage module by removing unnecessary imports and functions.
This commit is contained in:
@@ -7,8 +7,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
vdb = None
|
vdb = None
|
||||||
|
|
||||||
from . import scipy
|
from scipy import ndimage as ndi
|
||||||
from . import scipy as ndi
|
|
||||||
|
|
||||||
# 3rd-party
|
# 3rd-party
|
||||||
import transforms3d
|
import transforms3d
|
||||||
@@ -83,21 +82,17 @@ class Layer():
|
|||||||
for f in range(self.frame_count):
|
for f in range(self.frame_count):
|
||||||
mask_frame = mask[f, :, :, :]
|
mask_frame = mask[f, :, :, :]
|
||||||
if smooth > 0:
|
if smooth > 0:
|
||||||
mask_frame = scipy.median_filter(mask_frame.astype(np.float32),
|
mask_frame = ndi.median_filter(mask_frame.astype(np.float32),
|
||||||
mode="nearest",
|
mode="nearest",
|
||||||
size=smooth)
|
size=smooth)
|
||||||
# mask_frame = scipy.median_filter(
|
|
||||||
# mask_frame.astype(np.float32), size=2)
|
|
||||||
mask_frames += (mask_frame,)
|
mask_frames += (mask_frame,)
|
||||||
elif mask.ndim == 3:
|
elif mask.ndim == 3:
|
||||||
for f in range(self.frame_count):
|
for f in range(self.frame_count):
|
||||||
mask_frame = mask[:, :, :]
|
mask_frame = mask[:, :, :]
|
||||||
if smooth > 0:
|
if smooth > 0:
|
||||||
mask_frame = scipy.median_filter(mask_frame.astype(np.float32),
|
mask_frame = ndi.median_filter(mask_frame.astype(np.float32),
|
||||||
mode="nearest",
|
mode="nearest",
|
||||||
size=smooth)
|
size=smooth)
|
||||||
# mask_frame = scipy.median_filter(
|
|
||||||
# mask_frame.astype(np.float32), size=2)
|
|
||||||
mask_frames += (mask_frame,)
|
mask_frames += (mask_frame,)
|
||||||
else:
|
else:
|
||||||
raise Exception("Mask shape order should be TXYZ or XYZ")
|
raise Exception("Mask shape order should be TXYZ or XYZ")
|
||||||
@@ -127,7 +122,7 @@ class Layer():
|
|||||||
|
|
||||||
frame = data[f, :, :, :, :]
|
frame = data[f, :, :, :, :]
|
||||||
if smooth > 0:
|
if smooth > 0:
|
||||||
frame = scipy.median_filter(frame.astype(np.float32),
|
frame = ndi.median_filter(frame.astype(np.float32),
|
||||||
mode="nearest",
|
mode="nearest",
|
||||||
size=smooth)
|
size=smooth)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
from ._interpolation import zoom
|
|
||||||
from ._filters import (gaussian_filter,
|
|
||||||
median_filter,
|
|
||||||
maximum_filter,
|
|
||||||
minimum_filter)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,314 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from . import _ni_support
|
|
||||||
from . import _nd_image
|
|
||||||
from ._utils import normalize_axis_index
|
|
||||||
|
|
||||||
|
|
||||||
def _prepad_for_spline_filter(input, mode, cval):
|
|
||||||
if mode in ['nearest', 'grid-constant']:
|
|
||||||
npad = 12
|
|
||||||
if mode == 'grid-constant':
|
|
||||||
padded = np.pad(input, npad, mode='constant',
|
|
||||||
constant_values=cval)
|
|
||||||
elif mode == 'nearest':
|
|
||||||
padded = np.pad(input, npad, mode='edge')
|
|
||||||
else:
|
|
||||||
# other modes have exact boundary conditions implemented so
|
|
||||||
# no prepadding is needed
|
|
||||||
npad = 0
|
|
||||||
padded = input
|
|
||||||
return padded, npad
|
|
||||||
|
|
||||||
|
|
||||||
def spline_filter1d(input, order=3, axis=-1, output=np.float64,
|
|
||||||
mode='mirror'):
|
|
||||||
"""
|
|
||||||
Calculate a 1-D spline filter along the given axis.
|
|
||||||
|
|
||||||
The lines of the array along the given axis are filtered by a
|
|
||||||
spline filter. The order of the spline must be >= 2 and <= 5.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
%(input)s
|
|
||||||
order : int, optional
|
|
||||||
The order of the spline, default is 3.
|
|
||||||
axis : int, optional
|
|
||||||
The axis along which the spline filter is applied. Default is the last
|
|
||||||
axis.
|
|
||||||
output : ndarray or dtype, optional
|
|
||||||
The array in which to place the output, or the dtype of the returned
|
|
||||||
array. Default is ``numpy.float64``.
|
|
||||||
%(mode_interp_mirror)s
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
spline_filter1d : ndarray
|
|
||||||
The filtered input.
|
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
spline_filter : Multidimensional spline filter.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
All of the interpolation functions in `ndimage` do spline interpolation of
|
|
||||||
the input image. If using B-splines of `order > 1`, the input image
|
|
||||||
values have to be converted to B-spline coefficients first, which is
|
|
||||||
done by applying this 1-D filter sequentially along all
|
|
||||||
axes of the input. All functions that require B-spline coefficients
|
|
||||||
will automatically filter their inputs, a behavior controllable with
|
|
||||||
the `prefilter` keyword argument. For functions that accept a `mode`
|
|
||||||
parameter, the result will only be correct if it matches the `mode`
|
|
||||||
used when filtering.
|
|
||||||
|
|
||||||
For complex-valued `input`, this function processes the real and imaginary
|
|
||||||
components independently.
|
|
||||||
|
|
||||||
.. versionadded:: 1.6.0
|
|
||||||
Complex-valued support added.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
We can filter an image using 1-D spline along the given axis:
|
|
||||||
|
|
||||||
>>> from scipy.ndimage import spline_filter1d
|
|
||||||
>>> import numpy as np
|
|
||||||
>>> import matplotlib.pyplot as plt
|
|
||||||
>>> orig_img = np.eye(20) # create an image
|
|
||||||
>>> orig_img[10, :] = 1.0
|
|
||||||
>>> sp_filter_axis_0 = spline_filter1d(orig_img, axis=0)
|
|
||||||
>>> sp_filter_axis_1 = spline_filter1d(orig_img, axis=1)
|
|
||||||
>>> f, ax = plt.subplots(1, 3, sharex=True)
|
|
||||||
>>> for ind, data in enumerate([[orig_img, "original image"],
|
|
||||||
... [sp_filter_axis_0, "spline filter (axis=0)"],
|
|
||||||
... [sp_filter_axis_1, "spline filter (axis=1)"]]):
|
|
||||||
... ax[ind].imshow(data[0], cmap='gray_r')
|
|
||||||
... ax[ind].set_title(data[1])
|
|
||||||
>>> plt.tight_layout()
|
|
||||||
>>> plt.show()
|
|
||||||
|
|
||||||
"""
|
|
||||||
if order < 0 or order > 5:
|
|
||||||
raise RuntimeError('spline order not supported')
|
|
||||||
input = np.asarray(input)
|
|
||||||
complex_output = np.iscomplexobj(input)
|
|
||||||
output = _ni_support._get_output(output, input,
|
|
||||||
complex_output=complex_output)
|
|
||||||
if complex_output:
|
|
||||||
spline_filter1d(input.real, order, axis, output.real, mode)
|
|
||||||
spline_filter1d(input.imag, order, axis, output.imag, mode)
|
|
||||||
return output
|
|
||||||
if order in [0, 1]:
|
|
||||||
output[...] = np.array(input)
|
|
||||||
else:
|
|
||||||
mode = _ni_support._extend_mode_to_code(mode)
|
|
||||||
axis = normalize_axis_index(axis, input.ndim)
|
|
||||||
_nd_image.spline_filter1d(input, order, axis, output, mode)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def spline_filter(input, order=3, output=np.float64, mode='mirror'):
|
|
||||||
"""
|
|
||||||
Multidimensional spline filter.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
%(input)s
|
|
||||||
order : int, optional
|
|
||||||
The order of the spline, default is 3.
|
|
||||||
output : ndarray or dtype, optional
|
|
||||||
The array in which to place the output, or the dtype of the returned
|
|
||||||
array. Default is ``numpy.float64``.
|
|
||||||
%(mode_interp_mirror)s
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
spline_filter : ndarray
|
|
||||||
Filtered array. Has the same shape as `input`.
|
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
spline_filter1d : Calculate a 1-D spline filter along the given axis.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The multidimensional filter is implemented as a sequence of
|
|
||||||
1-D spline filters. The intermediate arrays are stored
|
|
||||||
in the same data type as the output. Therefore, for output types
|
|
||||||
with a limited precision, the results may be imprecise because
|
|
||||||
intermediate results may be stored with insufficient precision.
|
|
||||||
|
|
||||||
For complex-valued `input`, this function processes the real and imaginary
|
|
||||||
components independently.
|
|
||||||
|
|
||||||
.. versionadded:: 1.6.0
|
|
||||||
Complex-valued support added.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
We can filter an image using multidimentional splines:
|
|
||||||
|
|
||||||
>>> from scipy.ndimage import spline_filter
|
|
||||||
>>> import numpy as np
|
|
||||||
>>> import matplotlib.pyplot as plt
|
|
||||||
>>> orig_img = np.eye(20) # create an image
|
|
||||||
>>> orig_img[10, :] = 1.0
|
|
||||||
>>> sp_filter = spline_filter(orig_img, order=3)
|
|
||||||
>>> f, ax = plt.subplots(1, 2, sharex=True)
|
|
||||||
>>> for ind, data in enumerate([[orig_img, "original image"],
|
|
||||||
... [sp_filter, "spline filter"]]):
|
|
||||||
... ax[ind].imshow(data[0], cmap='gray_r')
|
|
||||||
... ax[ind].set_title(data[1])
|
|
||||||
>>> plt.tight_layout()
|
|
||||||
>>> plt.show()
|
|
||||||
|
|
||||||
"""
|
|
||||||
if order < 2 or order > 5:
|
|
||||||
raise RuntimeError('spline order not supported')
|
|
||||||
input = np.asarray(input)
|
|
||||||
complex_output = np.iscomplexobj(input)
|
|
||||||
output = _ni_support._get_output(output, input,
|
|
||||||
complex_output=complex_output)
|
|
||||||
if complex_output:
|
|
||||||
spline_filter(input.real, order, output.real, mode)
|
|
||||||
spline_filter(input.imag, order, output.imag, mode)
|
|
||||||
return output
|
|
||||||
if order not in [0, 1] and input.ndim > 0:
|
|
||||||
for axis in range(input.ndim):
|
|
||||||
spline_filter1d(input, order, axis, output=output, mode=mode)
|
|
||||||
input = output
|
|
||||||
else:
|
|
||||||
output[...] = input[...]
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0,
|
|
||||||
prefilter=True, *, grid_mode=False):
|
|
||||||
"""
|
|
||||||
Zoom an array.
|
|
||||||
|
|
||||||
The array is zoomed using spline interpolation of the requested order.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
%(input)s
|
|
||||||
zoom : float or sequence
|
|
||||||
The zoom factor along the axes. If a float, `zoom` is the same for each
|
|
||||||
axis. If a sequence, `zoom` should contain one value for each axis.
|
|
||||||
%(output)s
|
|
||||||
order : int, optional
|
|
||||||
The order of the spline interpolation, default is 3.
|
|
||||||
The order has to be in the range 0-5.
|
|
||||||
%(mode_interp_constant)s
|
|
||||||
%(cval)s
|
|
||||||
%(prefilter)s
|
|
||||||
grid_mode : bool, optional
|
|
||||||
If False, the distance from the pixel centers is zoomed. Otherwise, the
|
|
||||||
distance including the full pixel extent is used. For example, a 1d
|
|
||||||
signal of length 5 is considered to have length 4 when `grid_mode` is
|
|
||||||
False, but length 5 when `grid_mode` is True. See the following
|
|
||||||
visual illustration:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
| pixel 1 | pixel 2 | pixel 3 | pixel 4 | pixel 5 |
|
|
||||||
|<-------------------------------------->|
|
|
||||||
vs.
|
|
||||||
|<----------------------------------------------->|
|
|
||||||
|
|
||||||
The starting point of the arrow in the diagram above corresponds to
|
|
||||||
coordinate location 0 in each mode.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
zoom : ndarray
|
|
||||||
The zoomed input.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
For complex-valued `input`, this function zooms the real and imaginary
|
|
||||||
components independently.
|
|
||||||
|
|
||||||
.. versionadded:: 1.6.0
|
|
||||||
Complex-valued support added.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
>>> from scipy import ndimage, datasets
|
|
||||||
>>> import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
>>> fig = plt.figure()
|
|
||||||
>>> ax1 = fig.add_subplot(121) # left side
|
|
||||||
>>> ax2 = fig.add_subplot(122) # right side
|
|
||||||
>>> ascent = datasets.ascent()
|
|
||||||
>>> result = ndimage.zoom(ascent, 3.0)
|
|
||||||
>>> ax1.imshow(ascent, vmin=0, vmax=255)
|
|
||||||
>>> ax2.imshow(result, vmin=0, vmax=255)
|
|
||||||
>>> plt.show()
|
|
||||||
|
|
||||||
>>> print(ascent.shape)
|
|
||||||
(512, 512)
|
|
||||||
|
|
||||||
>>> print(result.shape)
|
|
||||||
(1536, 1536)
|
|
||||||
"""
|
|
||||||
if order < 0 or order > 5:
|
|
||||||
raise RuntimeError('spline order not supported')
|
|
||||||
input = np.asarray(input)
|
|
||||||
if input.ndim < 1:
|
|
||||||
raise RuntimeError('input and output rank must be > 0')
|
|
||||||
zoom = _ni_support._normalize_sequence(zoom, input.ndim)
|
|
||||||
output_shape = tuple(
|
|
||||||
[int(round(ii * jj)) for ii, jj in zip(input.shape, zoom)])
|
|
||||||
complex_output = np.iscomplexobj(input)
|
|
||||||
output = _ni_support._get_output(output, input, shape=output_shape,
|
|
||||||
complex_output=complex_output)
|
|
||||||
if complex_output:
|
|
||||||
# import under different name to avoid confusion with zoom parameter
|
|
||||||
from scipy.ndimage._interpolation import zoom as _zoom
|
|
||||||
|
|
||||||
kwargs = dict(order=order, mode=mode, prefilter=prefilter)
|
|
||||||
_zoom(input.real, zoom, output=output.real,
|
|
||||||
cval=np.real(cval), **kwargs)
|
|
||||||
_zoom(input.imag, zoom, output=output.imag,
|
|
||||||
cval=np.imag(cval), **kwargs)
|
|
||||||
return output
|
|
||||||
if prefilter and order > 1:
|
|
||||||
padded, npad = _prepad_for_spline_filter(input, mode, cval)
|
|
||||||
filtered = spline_filter(padded, order, output=np.float64, mode=mode)
|
|
||||||
else:
|
|
||||||
npad = 0
|
|
||||||
filtered = input
|
|
||||||
if grid_mode:
|
|
||||||
# warn about modes that may have surprising behavior
|
|
||||||
suggest_mode = None
|
|
||||||
if mode == 'constant':
|
|
||||||
suggest_mode = 'grid-constant'
|
|
||||||
elif mode == 'wrap':
|
|
||||||
suggest_mode = 'grid-wrap'
|
|
||||||
if suggest_mode is not None:
|
|
||||||
warnings.warn(
|
|
||||||
(f"It is recommended to use mode = {suggest_mode} instead of {mode} "
|
|
||||||
f"when grid_mode is True."),
|
|
||||||
stacklevel=2
|
|
||||||
)
|
|
||||||
mode = _ni_support._extend_mode_to_code(mode)
|
|
||||||
|
|
||||||
zoom_div = np.array(output_shape)
|
|
||||||
zoom_nominator = np.array(input.shape)
|
|
||||||
if not grid_mode:
|
|
||||||
zoom_div -= 1
|
|
||||||
zoom_nominator -= 1
|
|
||||||
|
|
||||||
# Zooming to infinite values is unpredictable, so just choose
|
|
||||||
# zoom factor 1 instead
|
|
||||||
zoom = np.divide(zoom_nominator, zoom_div,
|
|
||||||
out=np.ones_like(input.shape, dtype=np.float64),
|
|
||||||
where=zoom_div != 0)
|
|
||||||
zoom = np.ascontiguousarray(zoom)
|
|
||||||
_nd_image.zoom_shift(filtered, zoom, None, output, order, mode, cval, npad,
|
|
||||||
grid_mode)
|
|
||||||
return output
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
# Copyright (C) 2003-2005 Peter J. Verveer
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# 2. Redistributions in binary form must reproduce the above
|
|
||||||
# copyright notice, this list of conditions and the following
|
|
||||||
# disclaimer in the documentation and/or other materials provided
|
|
||||||
# with the distribution.
|
|
||||||
#
|
|
||||||
# 3. The name of the author may not be used to endorse or promote
|
|
||||||
# products derived from this software without specific prior
|
|
||||||
# written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
||||||
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
||||||
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
||||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
from collections.abc import Iterable
|
|
||||||
import operator
|
|
||||||
import warnings
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
def _extend_mode_to_code(mode):
|
|
||||||
"""Convert an extension mode to the corresponding integer code.
|
|
||||||
"""
|
|
||||||
if mode == 'nearest':
|
|
||||||
return 0
|
|
||||||
elif mode == 'wrap':
|
|
||||||
return 1
|
|
||||||
elif mode in ['reflect', 'grid-mirror']:
|
|
||||||
return 2
|
|
||||||
elif mode == 'mirror':
|
|
||||||
return 3
|
|
||||||
elif mode == 'constant':
|
|
||||||
return 4
|
|
||||||
elif mode == 'grid-wrap':
|
|
||||||
return 5
|
|
||||||
elif mode == 'grid-constant':
|
|
||||||
return 6
|
|
||||||
else:
|
|
||||||
raise RuntimeError('boundary mode not supported')
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_sequence(input, rank):
|
|
||||||
"""If input is a scalar, create a sequence of length equal to the
|
|
||||||
rank by duplicating the input. If input is a sequence,
|
|
||||||
check if its length is equal to the length of array.
|
|
||||||
"""
|
|
||||||
is_str = isinstance(input, str)
|
|
||||||
if not is_str and isinstance(input, Iterable):
|
|
||||||
normalized = list(input)
|
|
||||||
if len(normalized) != rank:
|
|
||||||
err = "sequence argument must have length equal to input rank"
|
|
||||||
raise RuntimeError(err)
|
|
||||||
else:
|
|
||||||
normalized = [input] * rank
|
|
||||||
return normalized
|
|
||||||
|
|
||||||
|
|
||||||
def _get_output(output, input, shape=None, complex_output=False):
|
|
||||||
if shape is None:
|
|
||||||
shape = input.shape
|
|
||||||
if output is None:
|
|
||||||
if not complex_output:
|
|
||||||
output = np.zeros(shape, dtype=input.dtype.name)
|
|
||||||
else:
|
|
||||||
complex_type = np.promote_types(input.dtype, np.complex64)
|
|
||||||
output = np.zeros(shape, dtype=complex_type)
|
|
||||||
elif isinstance(output, (type, np.dtype)):
|
|
||||||
# Classes (like `np.float32`) and dtypes are interpreted as dtype
|
|
||||||
if complex_output and np.dtype(output).kind != 'c':
|
|
||||||
warnings.warn(
|
|
||||||
"promoting specified output dtype to complex", stacklevel=3)
|
|
||||||
output = np.promote_types(output, np.complex64)
|
|
||||||
output = np.zeros(shape, dtype=output)
|
|
||||||
elif isinstance(output, str):
|
|
||||||
output = np.dtype(output)
|
|
||||||
if complex_output and output.kind != 'c':
|
|
||||||
raise RuntimeError("output must have complex dtype")
|
|
||||||
elif not issubclass(output.type, np.number):
|
|
||||||
raise RuntimeError("output must have numeric dtype")
|
|
||||||
output = np.zeros(shape, dtype=output)
|
|
||||||
elif output.shape != shape:
|
|
||||||
raise RuntimeError("output shape not correct")
|
|
||||||
elif complex_output and output.dtype.kind != 'c':
|
|
||||||
raise RuntimeError("output must have complex dtype")
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def _check_axes(axes, ndim):
|
|
||||||
if axes is None:
|
|
||||||
return tuple(range(ndim))
|
|
||||||
elif np.isscalar(axes):
|
|
||||||
axes = (operator.index(axes),)
|
|
||||||
elif isinstance(axes, Iterable):
|
|
||||||
for ax in axes:
|
|
||||||
axes = tuple(operator.index(ax) for ax in axes)
|
|
||||||
if ax < -ndim or ax > ndim - 1:
|
|
||||||
raise ValueError(f"specified axis: {ax} is out of range")
|
|
||||||
axes = tuple(ax % ndim if ax < 0 else ax for ax in axes)
|
|
||||||
else:
|
|
||||||
message = "axes must be an integer, iterable of integers, or None"
|
|
||||||
raise ValueError(message)
|
|
||||||
if len(tuple(set(axes))) != len(axes):
|
|
||||||
raise ValueError("axes must be unique")
|
|
||||||
return axes
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
def normalize_axis_index(axis, ndim):
|
|
||||||
# Check if `axis` is in the correct range and normalize it
|
|
||||||
if axis < -ndim or axis >= ndim:
|
|
||||||
msg = f"axis {axis} is out of bounds for array of dimension {ndim}"
|
|
||||||
raise Exception(msg)
|
|
||||||
|
|
||||||
if axis < 0:
|
|
||||||
axis = axis + ndim
|
|
||||||
return axis
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from ._warps import resize
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_float(image, preserve_range):
|
|
||||||
"""Convert input image to float image with the appropriate range.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
preserve_range : bool
|
|
||||||
Determines if the range of the image should be kept or transformed
|
|
||||||
using img_as_float. Also see
|
|
||||||
https://scikit-image.org/docs/dev/user_guide/data_types.html
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
* Input images with `float32` data type are not upcast.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
image : ndarray
|
|
||||||
Transformed version of the input.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if image.dtype == np.float16:
|
|
||||||
return image.astype(np.float32)
|
|
||||||
if preserve_range:
|
|
||||||
# Convert image to double only if it is not single or double
|
|
||||||
# precision float
|
|
||||||
if image.dtype.char not in 'df':
|
|
||||||
image = image.astype(float)
|
|
||||||
else:
|
|
||||||
from .dtype import img_as_float
|
|
||||||
|
|
||||||
image = img_as_float(image)
|
|
||||||
return image
|
|
||||||
@@ -1,319 +0,0 @@
|
|||||||
import warnings
|
|
||||||
import numpy as np
|
|
||||||
from .. import scipy as ndi
|
|
||||||
from ._utils import convert_to_float
|
|
||||||
|
|
||||||
|
|
||||||
def _clip_warp_output(input_image, output_image, mode, cval, clip):
|
|
||||||
"""Clip output image to range of values of input image.
|
|
||||||
|
|
||||||
Note that this function modifies the values of `output_image` in-place
|
|
||||||
and it is only modified if ``clip=True``.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
input_image : ndarray
|
|
||||||
Input image.
|
|
||||||
output_image : ndarray
|
|
||||||
Output image, which is modified in-place.
|
|
||||||
|
|
||||||
Other parameters
|
|
||||||
----------------
|
|
||||||
mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}
|
|
||||||
Points outside the boundaries of the input are filled according
|
|
||||||
to the given mode. Modes match the behaviour of `numpy.pad`.
|
|
||||||
cval : float
|
|
||||||
Used in conjunction with mode 'constant', the value outside
|
|
||||||
the image boundaries.
|
|
||||||
clip : bool
|
|
||||||
Whether to clip the output to the range of values of the input image.
|
|
||||||
This is enabled by default, since higher order interpolation may
|
|
||||||
produce values outside the given input range.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if clip:
|
|
||||||
min_val = np.min(input_image)
|
|
||||||
if np.isnan(min_val):
|
|
||||||
# NaNs detected, use NaN-safe min/max
|
|
||||||
min_func = np.nanmin
|
|
||||||
max_func = np.nanmax
|
|
||||||
min_val = min_func(input_image)
|
|
||||||
else:
|
|
||||||
min_func = np.min
|
|
||||||
max_func = np.max
|
|
||||||
max_val = max_func(input_image)
|
|
||||||
|
|
||||||
# Check if cval has been used such that it expands the effective input
|
|
||||||
# range
|
|
||||||
preserve_cval = (
|
|
||||||
mode == 'constant'
|
|
||||||
and not min_val <= cval <= max_val
|
|
||||||
and min_func(output_image) <= cval <= max_func(output_image)
|
|
||||||
)
|
|
||||||
|
|
||||||
# expand min/max range to account for cval
|
|
||||||
if preserve_cval:
|
|
||||||
# cast cval to the same dtype as the input image
|
|
||||||
cval = input_image.dtype.type(cval)
|
|
||||||
min_val = min(min_val, cval)
|
|
||||||
max_val = max(max_val, cval)
|
|
||||||
|
|
||||||
# Convert array-like types to ndarrays (gh-7159)
|
|
||||||
min_val, max_val = np.asarray(min_val), np.asarray(max_val)
|
|
||||||
np.clip(output_image, min_val, max_val, out=output_image)
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_interpolation_order(image_dtype, order):
|
|
||||||
"""Validate and return spline interpolation's order.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image_dtype : dtype
|
|
||||||
Image dtype.
|
|
||||||
order : int, optional
|
|
||||||
The order of the spline interpolation. The order has to be in
|
|
||||||
the range 0-5. See `skimage.transform.warp` for detail.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
order : int
|
|
||||||
if input order is None, returns 0 if image_dtype is bool and 1
|
|
||||||
otherwise. Otherwise, image_dtype is checked and input order
|
|
||||||
is validated accordingly (order > 0 is not supported for bool
|
|
||||||
image dtype)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
if order is None:
|
|
||||||
return 0 if image_dtype == bool else 1
|
|
||||||
|
|
||||||
if order < 0 or order > 5:
|
|
||||||
raise ValueError(
|
|
||||||
"Spline interpolation order has to be in the " "range 0-5.")
|
|
||||||
|
|
||||||
if image_dtype == bool and order != 0:
|
|
||||||
raise ValueError(
|
|
||||||
"Input image dtype is bool. Interpolation is not defined "
|
|
||||||
"with bool data type. Please set order to 0 or explicitly "
|
|
||||||
"cast input image to another data type."
|
|
||||||
)
|
|
||||||
|
|
||||||
return order
|
|
||||||
|
|
||||||
|
|
||||||
def _preprocess_resize_output_shape(image, output_shape):
|
|
||||||
"""Validate resize output shape according to input image.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image: ndarray
|
|
||||||
Image to be resized.
|
|
||||||
output_shape: iterable
|
|
||||||
Size of the generated output image `(rows, cols[, ...][, dim])`. If
|
|
||||||
`dim` is not provided, the number of channels is preserved.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
image: ndarray
|
|
||||||
The input image, but with additional singleton dimensions appended in
|
|
||||||
the case where ``len(output_shape) > input.ndim``.
|
|
||||||
output_shape: tuple
|
|
||||||
The output image converted to tuple.
|
|
||||||
|
|
||||||
Raises
|
|
||||||
------
|
|
||||||
ValueError:
|
|
||||||
If output_shape length is smaller than the image number of
|
|
||||||
dimensions
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The input image is reshaped if its number of dimensions is not
|
|
||||||
equal to output_shape_length.
|
|
||||||
|
|
||||||
"""
|
|
||||||
output_shape = tuple(output_shape)
|
|
||||||
output_ndim = len(output_shape)
|
|
||||||
input_shape = image.shape
|
|
||||||
if output_ndim > image.ndim:
|
|
||||||
# append dimensions to input_shape
|
|
||||||
input_shape += (1,) * (output_ndim - image.ndim)
|
|
||||||
image = np.reshape(image, input_shape)
|
|
||||||
elif output_ndim == image.ndim - 1:
|
|
||||||
# multichannel case: append shape of last axis
|
|
||||||
output_shape = output_shape + (image.shape[-1],)
|
|
||||||
elif output_ndim < image.ndim:
|
|
||||||
raise ValueError(
|
|
||||||
"output_shape length cannot be smaller than the "
|
|
||||||
"image number of dimensions"
|
|
||||||
)
|
|
||||||
|
|
||||||
return image, output_shape
|
|
||||||
|
|
||||||
|
|
||||||
def _to_ndimage_mode(mode):
|
|
||||||
"""Convert from `numpy.pad` mode name to the corresponding ndimage mode."""
|
|
||||||
mode_translation_dict = dict(
|
|
||||||
constant='constant',
|
|
||||||
edge='nearest',
|
|
||||||
symmetric='reflect',
|
|
||||||
reflect='mirror',
|
|
||||||
wrap='wrap',
|
|
||||||
)
|
|
||||||
if mode not in mode_translation_dict:
|
|
||||||
raise ValueError(
|
|
||||||
f"Unknown mode: '{mode}', or cannot translate mode. The "
|
|
||||||
f"mode should be one of 'constant', 'edge', 'symmetric', "
|
|
||||||
f"'reflect', or 'wrap'. See the documentation of numpy.pad for "
|
|
||||||
f"more info."
|
|
||||||
)
|
|
||||||
return _fix_ndimage_mode(mode_translation_dict[mode])
|
|
||||||
|
|
||||||
|
|
||||||
def _fix_ndimage_mode(mode):
|
|
||||||
# SciPy 1.6.0 introduced grid variants of constant and wrap which
|
|
||||||
# have less surprising behavior for images. Use these when available
|
|
||||||
grid_modes = {'constant': 'grid-constant', 'wrap': 'grid-wrap'}
|
|
||||||
return grid_modes.get(mode, mode)
|
|
||||||
|
|
||||||
|
|
||||||
def resize(
|
|
||||||
image,
|
|
||||||
output_shape,
|
|
||||||
order=None,
|
|
||||||
mode='reflect',
|
|
||||||
cval=0,
|
|
||||||
clip=True,
|
|
||||||
preserve_range=False,
|
|
||||||
anti_aliasing=None,
|
|
||||||
anti_aliasing_sigma=None,
|
|
||||||
):
|
|
||||||
"""Resize image to match a certain size.
|
|
||||||
|
|
||||||
Performs interpolation to up-size or down-size N-dimensional images. Note
|
|
||||||
that anti-aliasing should be enabled when down-sizing images to avoid
|
|
||||||
aliasing artifacts. For downsampling with an integer factor also see
|
|
||||||
`skimage.transform.downscale_local_mean`.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
output_shape : iterable
|
|
||||||
Size of the generated output image `(rows, cols[, ...][, dim])`. If
|
|
||||||
`dim` is not provided, the number of channels is preserved. In case the
|
|
||||||
number of input channels does not equal the number of output channels a
|
|
||||||
n-dimensional interpolation is applied.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
resized : ndarray
|
|
||||||
Resized version of the input.
|
|
||||||
|
|
||||||
Other parameters
|
|
||||||
----------------
|
|
||||||
order : int, optional
|
|
||||||
The order of the spline interpolation, default is 0 if
|
|
||||||
image.dtype is bool and 1 otherwise. The order has to be in
|
|
||||||
the range 0-5. See `skimage.transform.warp` for detail.
|
|
||||||
mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional
|
|
||||||
Points outside the boundaries of the input are filled according
|
|
||||||
to the given mode. Modes match the behaviour of `numpy.pad`.
|
|
||||||
cval : float, optional
|
|
||||||
Used in conjunction with mode 'constant', the value outside
|
|
||||||
the image boundaries.
|
|
||||||
clip : bool, optional
|
|
||||||
Whether to clip the output to the range of values of the input image.
|
|
||||||
This is enabled by default, since higher order interpolation may
|
|
||||||
produce values outside the given input range.
|
|
||||||
preserve_range : bool, optional
|
|
||||||
Whether to keep the original range of values. Otherwise, the input
|
|
||||||
image is converted according to the conventions of `img_as_float`.
|
|
||||||
Also see https://scikit-image.org/docs/dev/user_guide/data_types.html
|
|
||||||
anti_aliasing : bool, optional
|
|
||||||
Whether to apply a Gaussian filter to smooth the image prior
|
|
||||||
to downsampling. It is crucial to filter when downsampling
|
|
||||||
the image to avoid aliasing artifacts. If not specified, it is set to
|
|
||||||
True when downsampling an image whose data type is not bool.
|
|
||||||
It is also set to False when using nearest neighbor interpolation
|
|
||||||
(``order`` == 0) with integer input data type.
|
|
||||||
anti_aliasing_sigma : {float, tuple of floats}, optional
|
|
||||||
Standard deviation for Gaussian filtering used when anti-aliasing.
|
|
||||||
By default, this value is chosen as (s - 1) / 2 where s is the
|
|
||||||
downsampling factor, where s > 1. For the up-size case, s < 1, no
|
|
||||||
anti-aliasing is performed prior to rescaling.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
Modes 'reflect' and 'symmetric' are similar, but differ in whether the edge
|
|
||||||
pixels are duplicated during the reflection. As an example, if an array
|
|
||||||
has values [0, 1, 2] and was padded to the right by four values using
|
|
||||||
symmetric, the result would be [0, 1, 2, 2, 1, 0, 0], while for reflect it
|
|
||||||
would be [0, 1, 2, 1, 0, 1, 2].
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
>>> from skimage import data
|
|
||||||
>>> from skimage.transform import resize
|
|
||||||
>>> image = data.camera()
|
|
||||||
>>> resize(image, (100, 100)).shape
|
|
||||||
(100, 100)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
image, output_shape = _preprocess_resize_output_shape(image, output_shape)
|
|
||||||
input_shape = image.shape
|
|
||||||
input_type = image.dtype
|
|
||||||
|
|
||||||
if input_type == np.float16:
|
|
||||||
image = image.astype(np.float32)
|
|
||||||
|
|
||||||
if anti_aliasing is None:
|
|
||||||
anti_aliasing = (
|
|
||||||
not input_type == bool
|
|
||||||
and not (np.issubdtype(input_type, np.integer) and order == 0)
|
|
||||||
and any(x < y for x, y in zip(output_shape, input_shape))
|
|
||||||
)
|
|
||||||
|
|
||||||
if input_type == bool and anti_aliasing:
|
|
||||||
raise ValueError("anti_aliasing must be False for boolean images")
|
|
||||||
|
|
||||||
factors = np.divide(input_shape, output_shape)
|
|
||||||
order = _validate_interpolation_order(input_type, order)
|
|
||||||
if order > 0:
|
|
||||||
image = convert_to_float(image, preserve_range)
|
|
||||||
|
|
||||||
# Translate modes used by np.pad to those used by scipy.ndimage
|
|
||||||
ndi_mode = _to_ndimage_mode(mode)
|
|
||||||
if anti_aliasing:
|
|
||||||
if anti_aliasing_sigma is None:
|
|
||||||
anti_aliasing_sigma = np.maximum(0, (factors - 1) / 2)
|
|
||||||
else:
|
|
||||||
anti_aliasing_sigma = np.atleast_1d(anti_aliasing_sigma) * np.ones_like(
|
|
||||||
factors
|
|
||||||
)
|
|
||||||
if np.any(anti_aliasing_sigma < 0):
|
|
||||||
raise ValueError(
|
|
||||||
"Anti-aliasing standard deviation must be "
|
|
||||||
"greater than or equal to zero"
|
|
||||||
)
|
|
||||||
elif np.any((anti_aliasing_sigma > 0) & (factors <= 1)):
|
|
||||||
warnings.warn(
|
|
||||||
"Anti-aliasing standard deviation greater than zero but "
|
|
||||||
"not down-sampling along all axes",
|
|
||||||
stacklevel=2
|
|
||||||
)
|
|
||||||
filtered = ndi.gaussian_filter(
|
|
||||||
image, anti_aliasing_sigma, cval=cval, mode=ndi_mode
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
filtered = image
|
|
||||||
|
|
||||||
zoom_factors = [1 / f for f in factors]
|
|
||||||
out = ndi.zoom(
|
|
||||||
filtered, zoom_factors, order=order, mode=ndi_mode, cval=cval, grid_mode=True
|
|
||||||
)
|
|
||||||
|
|
||||||
_clip_warp_output(image, out, mode, cval, clip)
|
|
||||||
|
|
||||||
return out
|
|
||||||
@@ -1,605 +0,0 @@
|
|||||||
import warnings
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'img_as_float32',
|
|
||||||
'img_as_float64',
|
|
||||||
'img_as_float',
|
|
||||||
'img_as_int',
|
|
||||||
'img_as_uint',
|
|
||||||
'img_as_ubyte',
|
|
||||||
'img_as_bool',
|
|
||||||
'dtype_limits',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Some of these may or may not be aliases depending on architecture & platform
|
|
||||||
_integer_types = (
|
|
||||||
np.int8,
|
|
||||||
np.byte,
|
|
||||||
np.int16,
|
|
||||||
np.short,
|
|
||||||
np.int32,
|
|
||||||
np.int64,
|
|
||||||
np.longlong,
|
|
||||||
np.int_,
|
|
||||||
np.intp,
|
|
||||||
np.intc,
|
|
||||||
int,
|
|
||||||
np.uint8,
|
|
||||||
np.ubyte,
|
|
||||||
np.uint16,
|
|
||||||
np.ushort,
|
|
||||||
np.uint32,
|
|
||||||
np.uint64,
|
|
||||||
np.ulonglong,
|
|
||||||
np.uint,
|
|
||||||
np.uintp,
|
|
||||||
np.uintc,
|
|
||||||
)
|
|
||||||
_integer_ranges = {t: (np.iinfo(t).min, np.iinfo(t).max)
|
|
||||||
for t in _integer_types}
|
|
||||||
dtype_range = {
|
|
||||||
bool: (False, True),
|
|
||||||
np.bool_: (False, True),
|
|
||||||
float: (-1, 1),
|
|
||||||
np.float16: (-1, 1),
|
|
||||||
np.float32: (-1, 1),
|
|
||||||
np.float64: (-1, 1),
|
|
||||||
}
|
|
||||||
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.filterwarnings('ignore', category=DeprecationWarning)
|
|
||||||
|
|
||||||
# np.bool8 is a deprecated alias of np.bool_
|
|
||||||
if hasattr(np, 'bool8'):
|
|
||||||
dtype_range[np.bool8] = (False, True)
|
|
||||||
|
|
||||||
dtype_range.update(_integer_ranges)
|
|
||||||
|
|
||||||
_supported_types = list(dtype_range.keys())
|
|
||||||
|
|
||||||
|
|
||||||
def dtype_limits(image, clip_negative=False):
|
|
||||||
"""Return intensity limits, i.e. (min, max) tuple, of the image's dtype.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
clip_negative : bool, optional
|
|
||||||
If True, clip the negative range (i.e. return 0 for min intensity)
|
|
||||||
even if the image dtype allows negative values.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
imin, imax : tuple
|
|
||||||
Lower and upper intensity limits.
|
|
||||||
"""
|
|
||||||
imin, imax = dtype_range[image.dtype.type]
|
|
||||||
if clip_negative:
|
|
||||||
imin = 0
|
|
||||||
return imin, imax
|
|
||||||
|
|
||||||
|
|
||||||
def _dtype_itemsize(itemsize, *dtypes):
|
|
||||||
"""Return first of `dtypes` with itemsize greater than `itemsize`
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
itemsize: int
|
|
||||||
The data type object element size.
|
|
||||||
|
|
||||||
Other Parameters
|
|
||||||
----------------
|
|
||||||
*dtypes:
|
|
||||||
Any Object accepted by `np.dtype` to be converted to a data
|
|
||||||
type object
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dtype: data type object
|
|
||||||
First of `dtypes` with itemsize greater than `itemsize`.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return next(dt for dt in dtypes if np.dtype(dt).itemsize >= itemsize)
|
|
||||||
|
|
||||||
|
|
||||||
def _dtype_bits(kind, bits, itemsize=1):
|
|
||||||
"""Return dtype of `kind` that can store a `bits` wide unsigned int
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
kind: str
|
|
||||||
Data type kind.
|
|
||||||
bits: int
|
|
||||||
Desired number of bits.
|
|
||||||
itemsize: int
|
|
||||||
The data type object element size.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dtype: data type object
|
|
||||||
Data type of `kind` that can store a `bits` wide unsigned int
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
s = next(
|
|
||||||
i
|
|
||||||
for i in (itemsize,) + (2, 4, 8)
|
|
||||||
if bits < (i * 8) or (bits == (i * 8) and kind == 'u')
|
|
||||||
)
|
|
||||||
|
|
||||||
return np.dtype(kind + str(s))
|
|
||||||
|
|
||||||
|
|
||||||
def _scale(a, n, m, copy=True):
|
|
||||||
"""Scale an array of unsigned/positive integers from `n` to `m` bits.
|
|
||||||
|
|
||||||
Numbers can be represented exactly only if `m` is a multiple of `n`.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
a : ndarray
|
|
||||||
Input image array.
|
|
||||||
n : int
|
|
||||||
Number of bits currently used to encode the values in `a`.
|
|
||||||
m : int
|
|
||||||
Desired number of bits to encode the values in `out`.
|
|
||||||
copy : bool, optional
|
|
||||||
If True, allocates and returns new array. Otherwise, modifies
|
|
||||||
`a` in place.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : array
|
|
||||||
Output image array. Has the same kind as `a`.
|
|
||||||
"""
|
|
||||||
kind = a.dtype.kind
|
|
||||||
if n > m and a.max() < 2**m:
|
|
||||||
mnew = int(np.ceil(m / 2) * 2)
|
|
||||||
if mnew > m:
|
|
||||||
dtype = f'int{mnew}'
|
|
||||||
else:
|
|
||||||
dtype = f'uint{mnew}'
|
|
||||||
n = int(np.ceil(n / 2) * 2)
|
|
||||||
warn(
|
|
||||||
f'Downcasting {a.dtype} to {dtype} without scaling because max '
|
|
||||||
f'value {a.max()} fits in {dtype}',
|
|
||||||
stacklevel=3,
|
|
||||||
)
|
|
||||||
return a.astype(_dtype_bits(kind, m))
|
|
||||||
elif n == m:
|
|
||||||
return a.copy() if copy else a
|
|
||||||
elif n > m:
|
|
||||||
# downscale with precision loss
|
|
||||||
if copy:
|
|
||||||
b = np.empty(a.shape, _dtype_bits(kind, m))
|
|
||||||
np.floor_divide(a, 2 ** (n - m), out=b,
|
|
||||||
dtype=a.dtype, casting='unsafe')
|
|
||||||
return b
|
|
||||||
else:
|
|
||||||
a //= 2 ** (n - m)
|
|
||||||
return a
|
|
||||||
elif m % n == 0:
|
|
||||||
# exact upscale to a multiple of `n` bits
|
|
||||||
if copy:
|
|
||||||
b = np.empty(a.shape, _dtype_bits(kind, m))
|
|
||||||
np.multiply(a, (2**m - 1) // (2**n - 1), out=b, dtype=b.dtype)
|
|
||||||
return b
|
|
||||||
else:
|
|
||||||
a = a.astype(_dtype_bits(kind, m, a.dtype.itemsize), copy=False)
|
|
||||||
a *= (2**m - 1) // (2**n - 1)
|
|
||||||
return a
|
|
||||||
else:
|
|
||||||
# upscale to a multiple of `n` bits,
|
|
||||||
# then downscale with precision loss
|
|
||||||
o = (m // n + 1) * n
|
|
||||||
if copy:
|
|
||||||
b = np.empty(a.shape, _dtype_bits(kind, o))
|
|
||||||
np.multiply(a, (2**o - 1) // (2**n - 1), out=b, dtype=b.dtype)
|
|
||||||
b //= 2 ** (o - m)
|
|
||||||
return b
|
|
||||||
else:
|
|
||||||
a = a.astype(_dtype_bits(kind, o, a.dtype.itemsize), copy=False)
|
|
||||||
a *= (2**o - 1) // (2**n - 1)
|
|
||||||
a //= 2 ** (o - m)
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
def _convert(image, dtype, force_copy=False, uniform=False):
|
|
||||||
"""
|
|
||||||
Convert an image to the requested data-type.
|
|
||||||
|
|
||||||
Warnings are issued in case of precision loss, or when negative values
|
|
||||||
are clipped during conversion to unsigned integer types (sign loss).
|
|
||||||
|
|
||||||
Floating point values are expected to be normalized and will be clipped
|
|
||||||
to the range [0.0, 1.0] or [-1.0, 1.0] when converting to unsigned or
|
|
||||||
signed integers respectively.
|
|
||||||
|
|
||||||
Numbers are not shifted to the negative side when converting from
|
|
||||||
unsigned to signed integer types. Negative values will be clipped when
|
|
||||||
converting to unsigned integers.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
dtype : dtype
|
|
||||||
Target data-type.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
uniform : bool, optional
|
|
||||||
Uniformly quantize the floating point range to the integer range.
|
|
||||||
By default (uniform=False) floating point values are scaled and
|
|
||||||
rounded to the nearest integers, which minimizes back and forth
|
|
||||||
conversion errors.
|
|
||||||
|
|
||||||
.. versionchanged:: 0.15
|
|
||||||
``_convert`` no longer warns about possible precision or sign
|
|
||||||
information loss. See discussions on these warnings at:
|
|
||||||
https://github.com/scikit-image/scikit-image/issues/2602
|
|
||||||
https://github.com/scikit-image/scikit-image/issues/543#issuecomment-208202228
|
|
||||||
https://github.com/scikit-image/scikit-image/pull/3575
|
|
||||||
|
|
||||||
References
|
|
||||||
----------
|
|
||||||
.. [1] DirectX data conversion rules.
|
|
||||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd607323%28v=vs.85%29.aspx
|
|
||||||
.. [2] Data Conversions. In "OpenGL ES 2.0 Specification v2.0.25",
|
|
||||||
pp 7-8. Khronos Group, 2010.
|
|
||||||
.. [3] Proper treatment of pixels as integers. A.W. Paeth.
|
|
||||||
In "Graphics Gems I", pp 249-256. Morgan Kaufmann, 1990.
|
|
||||||
.. [4] Dirty Pixels. J. Blinn. In "Jim Blinn's corner: Dirty Pixels",
|
|
||||||
pp 47-57. Morgan Kaufmann, 1998.
|
|
||||||
|
|
||||||
"""
|
|
||||||
image = np.asarray(image)
|
|
||||||
dtypeobj_in = image.dtype
|
|
||||||
if dtype is np.floating:
|
|
||||||
dtypeobj_out = np.dtype('float64')
|
|
||||||
else:
|
|
||||||
dtypeobj_out = np.dtype(dtype)
|
|
||||||
dtype_in = dtypeobj_in.type
|
|
||||||
dtype_out = dtypeobj_out.type
|
|
||||||
kind_in = dtypeobj_in.kind
|
|
||||||
kind_out = dtypeobj_out.kind
|
|
||||||
itemsize_in = dtypeobj_in.itemsize
|
|
||||||
itemsize_out = dtypeobj_out.itemsize
|
|
||||||
|
|
||||||
# Below, we do an `issubdtype` check. Its purpose is to find out
|
|
||||||
# whether we can get away without doing any image conversion. This happens
|
|
||||||
# when:
|
|
||||||
#
|
|
||||||
# - the output and input dtypes are the same or
|
|
||||||
# - when the output is specified as a type, and the input dtype
|
|
||||||
# is a subclass of that type (e.g. `np.floating` will allow
|
|
||||||
# `float32` and `float64` arrays through)
|
|
||||||
|
|
||||||
if np.issubdtype(dtype_in, dtype):
|
|
||||||
if force_copy:
|
|
||||||
image = image.copy()
|
|
||||||
return image
|
|
||||||
|
|
||||||
if not (dtype_in in _supported_types and dtype_out in _supported_types):
|
|
||||||
raise ValueError(
|
|
||||||
f'Cannot convert from {dtypeobj_in} to ' f'{dtypeobj_out}.')
|
|
||||||
|
|
||||||
if kind_in in 'ui':
|
|
||||||
imin_in = np.iinfo(dtype_in).min
|
|
||||||
imax_in = np.iinfo(dtype_in).max
|
|
||||||
if kind_out in 'ui':
|
|
||||||
imin_out = np.iinfo(dtype_out).min
|
|
||||||
imax_out = np.iinfo(dtype_out).max
|
|
||||||
|
|
||||||
# any -> binary
|
|
||||||
if kind_out == 'b':
|
|
||||||
return image > dtype_in(dtype_range[dtype_in][1] / 2)
|
|
||||||
|
|
||||||
# binary -> any
|
|
||||||
if kind_in == 'b':
|
|
||||||
result = image.astype(dtype_out)
|
|
||||||
if kind_out != 'f':
|
|
||||||
result *= dtype_out(dtype_range[dtype_out][1])
|
|
||||||
return result
|
|
||||||
|
|
||||||
# float -> any
|
|
||||||
if kind_in == 'f':
|
|
||||||
if kind_out == 'f':
|
|
||||||
# float -> float
|
|
||||||
return image.astype(dtype_out)
|
|
||||||
|
|
||||||
if np.min(image) < -1.0 or np.max(image) > 1.0:
|
|
||||||
raise ValueError("Images of type float must be between -1 and 1.")
|
|
||||||
# floating point -> integer
|
|
||||||
# use float type that can represent output integer type
|
|
||||||
computation_type = _dtype_itemsize(
|
|
||||||
itemsize_out, dtype_in, np.float32, np.float64
|
|
||||||
)
|
|
||||||
|
|
||||||
if not uniform:
|
|
||||||
if kind_out == 'u':
|
|
||||||
image_out = np.multiply(
|
|
||||||
image, imax_out, dtype=computation_type)
|
|
||||||
else:
|
|
||||||
image_out = np.multiply(
|
|
||||||
image, (imax_out - imin_out) / 2, dtype=computation_type
|
|
||||||
)
|
|
||||||
image_out -= 1.0 / 2.0
|
|
||||||
np.rint(image_out, out=image_out)
|
|
||||||
np.clip(image_out, imin_out, imax_out, out=image_out)
|
|
||||||
elif kind_out == 'u':
|
|
||||||
image_out = np.multiply(
|
|
||||||
image, imax_out + 1, dtype=computation_type)
|
|
||||||
np.clip(image_out, 0, imax_out, out=image_out)
|
|
||||||
else:
|
|
||||||
image_out = np.multiply(
|
|
||||||
image, (imax_out - imin_out + 1.0) / 2.0, dtype=computation_type
|
|
||||||
)
|
|
||||||
np.floor(image_out, out=image_out)
|
|
||||||
np.clip(image_out, imin_out, imax_out, out=image_out)
|
|
||||||
return image_out.astype(dtype_out)
|
|
||||||
|
|
||||||
# signed/unsigned int -> float
|
|
||||||
if kind_out == 'f':
|
|
||||||
# use float type that can exactly represent input integers
|
|
||||||
computation_type = _dtype_itemsize(
|
|
||||||
itemsize_in, dtype_out, np.float32, np.float64
|
|
||||||
)
|
|
||||||
|
|
||||||
if kind_in == 'u':
|
|
||||||
# using np.divide or np.multiply doesn't copy the data
|
|
||||||
# until the computation time
|
|
||||||
image = np.multiply(image, 1.0 / imax_in, dtype=computation_type)
|
|
||||||
# DirectX uses this conversion also for signed ints
|
|
||||||
# if imin_in:
|
|
||||||
# np.maximum(image, -1.0, out=image)
|
|
||||||
elif kind_in == 'i':
|
|
||||||
# From DirectX conversions:
|
|
||||||
# The most negative value maps to -1.0f
|
|
||||||
# Every other value is converted to a float (call it c)
|
|
||||||
# and then result = c * (1.0f / (2⁽ⁿ⁻¹⁾-1)).
|
|
||||||
|
|
||||||
image = np.multiply(image, 1.0 / imax_in, dtype=computation_type)
|
|
||||||
np.maximum(image, -1.0, out=image)
|
|
||||||
|
|
||||||
else:
|
|
||||||
image = np.add(image, 0.5, dtype=computation_type)
|
|
||||||
image *= 2 / (imax_in - imin_in)
|
|
||||||
|
|
||||||
return np.asarray(image, dtype_out)
|
|
||||||
|
|
||||||
# unsigned int -> signed/unsigned int
|
|
||||||
if kind_in == 'u':
|
|
||||||
if kind_out == 'i':
|
|
||||||
# unsigned int -> signed int
|
|
||||||
image = _scale(image, 8 * itemsize_in, 8 * itemsize_out - 1)
|
|
||||||
return image.view(dtype_out)
|
|
||||||
else:
|
|
||||||
# unsigned int -> unsigned int
|
|
||||||
return _scale(image, 8 * itemsize_in, 8 * itemsize_out)
|
|
||||||
|
|
||||||
# signed int -> unsigned int
|
|
||||||
if kind_out == 'u':
|
|
||||||
image = _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out)
|
|
||||||
result = np.empty(image.shape, dtype_out)
|
|
||||||
np.maximum(image, 0, out=result, dtype=image.dtype, casting='unsafe')
|
|
||||||
return result
|
|
||||||
|
|
||||||
# signed int -> signed int
|
|
||||||
if itemsize_in > itemsize_out:
|
|
||||||
return _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out - 1)
|
|
||||||
|
|
||||||
image = image.astype(_dtype_bits('i', itemsize_out * 8))
|
|
||||||
image -= imin_in
|
|
||||||
image = _scale(image, 8 * itemsize_in, 8 * itemsize_out, copy=False)
|
|
||||||
image += imin_out
|
|
||||||
return image.astype(dtype_out)
|
|
||||||
|
|
||||||
|
|
||||||
def convert(image, dtype, force_copy=False, uniform=False):
|
|
||||||
warn(
|
|
||||||
"The use of this function is discouraged as its behavior may change "
|
|
||||||
"dramatically in scikit-image 1.0. This function will be removed "
|
|
||||||
"in scikit-image 1.0.",
|
|
||||||
FutureWarning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
return _convert(image=image, dtype=dtype, force_copy=force_copy, uniform=uniform)
|
|
||||||
|
|
||||||
|
|
||||||
if _convert.__doc__ is not None:
|
|
||||||
convert.__doc__ = (
|
|
||||||
_convert.__doc__
|
|
||||||
+ """
|
|
||||||
|
|
||||||
Warns
|
|
||||||
-----
|
|
||||||
FutureWarning:
|
|
||||||
.. versionadded:: 0.17
|
|
||||||
|
|
||||||
The use of this function is discouraged as its behavior may change
|
|
||||||
dramatically in scikit-image 1.0. This function will be removed
|
|
||||||
in scikit-image 1.0.
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_float32(image, force_copy=False):
|
|
||||||
"""Convert an image to single-precision (32-bit) floating point format.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of float32
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
|
|
||||||
converting from unsigned or signed datatypes, respectively.
|
|
||||||
If the input image has a float type, intensity values are not modified
|
|
||||||
and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, np.float32, force_copy)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_float64(image, force_copy=False):
|
|
||||||
"""Convert an image to double-precision (64-bit) floating point format.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of float64
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
|
|
||||||
converting from unsigned or signed datatypes, respectively.
|
|
||||||
If the input image has a float type, intensity values are not modified
|
|
||||||
and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, np.float64, force_copy)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_float(image, force_copy=False):
|
|
||||||
"""Convert an image to floating point format.
|
|
||||||
|
|
||||||
This function is similar to `img_as_float64`, but will not convert
|
|
||||||
lower-precision floating point arrays to `float64`.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of float
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
|
|
||||||
converting from unsigned or signed datatypes, respectively.
|
|
||||||
If the input image has a float type, intensity values are not modified
|
|
||||||
and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, np.floating, force_copy)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_uint(image, force_copy=False):
|
|
||||||
"""Convert an image to 16-bit unsigned integer format.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of uint16
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
Negative input values will be clipped.
|
|
||||||
Positive values are scaled between 0 and 65535.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, np.uint16, force_copy)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_int(image, force_copy=False):
|
|
||||||
"""Convert an image to 16-bit signed integer format.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of int16
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The values are scaled between -32768 and 32767.
|
|
||||||
If the input data-type is positive-only (e.g., uint8), then
|
|
||||||
the output image will still only have positive values.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, np.int16, force_copy)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_ubyte(image, force_copy=False):
|
|
||||||
"""Convert an image to 8-bit unsigned integer format.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of ubyte (uint8)
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
Negative input values will be clipped.
|
|
||||||
Positive values are scaled between 0 and 255.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, np.uint8, force_copy)
|
|
||||||
|
|
||||||
|
|
||||||
def img_as_bool(image, force_copy=False):
|
|
||||||
"""Convert an image to boolean format.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image : ndarray
|
|
||||||
Input image.
|
|
||||||
force_copy : bool, optional
|
|
||||||
Force a copy of the data, irrespective of its current dtype.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
out : ndarray of bool (`bool_`)
|
|
||||||
Output image.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
-----
|
|
||||||
The upper half of the input dtype's positive range is True, and the lower
|
|
||||||
half is False. All negative values (if present) are False.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _convert(image, bool, force_copy)
|
|
||||||
Reference in New Issue
Block a user