|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import itertools |
|
import warnings |
|
|
|
import numpy as np |
|
from scipy._lib._util import normalize_axis_index |
|
|
|
from scipy import special |
|
from . import _ni_support |
|
from . import _nd_image |
|
from ._ni_docstrings import docfiller |
|
|
|
|
|
__all__ = ['spline_filter1d', 'spline_filter', 'geometric_transform', |
|
'map_coordinates', 'affine_transform', 'shift', 'zoom', 'rotate'] |
|
|
|
|
|
@docfiller |
|
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 |
|
|
|
@docfiller |
|
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 multidimensional 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 _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: |
|
|
|
|
|
npad = 0 |
|
padded = input |
|
return padded, npad |
|
|
|
|
|
@docfiller |
|
def geometric_transform(input, mapping, output_shape=None, |
|
output=None, order=3, |
|
mode='constant', cval=0.0, prefilter=True, |
|
extra_arguments=(), extra_keywords=None): |
|
""" |
|
Apply an arbitrary geometric transform. |
|
|
|
The given mapping function is used to find, for each point in the |
|
output, the corresponding coordinates in the input. The value of the |
|
input at those coordinates is determined by spline interpolation of |
|
the requested order. |
|
|
|
Parameters |
|
---------- |
|
%(input)s |
|
mapping : {callable, scipy.LowLevelCallable} |
|
A callable object that accepts a tuple of length equal to the output |
|
array rank, and returns the corresponding input coordinates as a tuple |
|
of length equal to the input array rank. |
|
output_shape : tuple of ints, optional |
|
Shape tuple. |
|
%(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 |
|
extra_arguments : tuple, optional |
|
Extra arguments passed to `mapping`. |
|
extra_keywords : dict, optional |
|
Extra keywords passed to `mapping`. |
|
|
|
Returns |
|
------- |
|
output : ndarray |
|
The filtered input. |
|
|
|
See Also |
|
-------- |
|
map_coordinates, affine_transform, spline_filter1d |
|
|
|
|
|
Notes |
|
----- |
|
This function also accepts low-level callback functions with one |
|
the following signatures and wrapped in `scipy.LowLevelCallable`: |
|
|
|
.. code:: c |
|
|
|
int mapping(npy_intp *output_coordinates, double *input_coordinates, |
|
int output_rank, int input_rank, void *user_data) |
|
int mapping(intptr_t *output_coordinates, double *input_coordinates, |
|
int output_rank, int input_rank, void *user_data) |
|
|
|
The calling function iterates over the elements of the output array, |
|
calling the callback function at each element. The coordinates of the |
|
current output element are passed through ``output_coordinates``. The |
|
callback function must return the coordinates at which the input must |
|
be interpolated in ``input_coordinates``. The rank of the input and |
|
output arrays are given by ``input_rank`` and ``output_rank`` |
|
respectively. ``user_data`` is the data pointer provided |
|
to `scipy.LowLevelCallable` as-is. |
|
|
|
The callback function must return an integer error status that is zero |
|
if something went wrong and one otherwise. If an error occurs, you should |
|
normally set the Python error status with an informative message |
|
before returning, otherwise a default error message is set by the |
|
calling function. |
|
|
|
In addition, some other low-level function pointer specifications |
|
are accepted, but these are for backward compatibility only and should |
|
not be used in new code. |
|
|
|
For complex-valued `input`, this function transforms the real and imaginary |
|
components independently. |
|
|
|
.. versionadded:: 1.6.0 |
|
Complex-valued support added. |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> from scipy.ndimage import geometric_transform |
|
>>> a = np.arange(12.).reshape((4, 3)) |
|
>>> def shift_func(output_coords): |
|
... return (output_coords[0] - 0.5, output_coords[1] - 0.5) |
|
... |
|
>>> geometric_transform(a, shift_func) |
|
array([[ 0. , 0. , 0. ], |
|
[ 0. , 1.362, 2.738], |
|
[ 0. , 4.812, 6.187], |
|
[ 0. , 8.263, 9.637]]) |
|
|
|
>>> b = [1, 2, 3, 4, 5] |
|
>>> def shift_func(output_coords): |
|
... return (output_coords[0] - 3,) |
|
... |
|
>>> geometric_transform(b, shift_func, mode='constant') |
|
array([0, 0, 0, 1, 2]) |
|
>>> geometric_transform(b, shift_func, mode='nearest') |
|
array([1, 1, 1, 1, 2]) |
|
>>> geometric_transform(b, shift_func, mode='reflect') |
|
array([3, 2, 1, 1, 2]) |
|
>>> geometric_transform(b, shift_func, mode='wrap') |
|
array([2, 3, 4, 1, 2]) |
|
|
|
""" |
|
if extra_keywords is None: |
|
extra_keywords = {} |
|
if order < 0 or order > 5: |
|
raise RuntimeError('spline order not supported') |
|
input = np.asarray(input) |
|
if output_shape is None: |
|
output_shape = input.shape |
|
if input.ndim < 1 or len(output_shape) < 1: |
|
raise RuntimeError('input and output rank must be > 0') |
|
complex_output = np.iscomplexobj(input) |
|
output = _ni_support._get_output(output, input, shape=output_shape, |
|
complex_output=complex_output) |
|
if complex_output: |
|
kwargs = dict(order=order, mode=mode, prefilter=prefilter, |
|
output_shape=output_shape, |
|
extra_arguments=extra_arguments, |
|
extra_keywords=extra_keywords) |
|
geometric_transform(input.real, mapping, output=output.real, |
|
cval=np.real(cval), **kwargs) |
|
geometric_transform(input.imag, mapping, 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 |
|
mode = _ni_support._extend_mode_to_code(mode) |
|
_nd_image.geometric_transform(filtered, mapping, None, None, None, output, |
|
order, mode, cval, npad, extra_arguments, |
|
extra_keywords) |
|
return output |
|
|
|
|
|
@docfiller |
|
def map_coordinates(input, coordinates, output=None, order=3, |
|
mode='constant', cval=0.0, prefilter=True): |
|
""" |
|
Map the input array to new coordinates by interpolation. |
|
|
|
The array of coordinates is used to find, for each point in the output, |
|
the corresponding coordinates in the input. The value of the input at |
|
those coordinates is determined by spline interpolation of the |
|
requested order. |
|
|
|
The shape of the output is derived from that of the coordinate |
|
array by dropping the first axis. The values of the array along |
|
the first axis are the coordinates in the input array at which the |
|
output value is found. |
|
|
|
Parameters |
|
---------- |
|
%(input)s |
|
coordinates : array_like |
|
The coordinates at which `input` is evaluated. |
|
%(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 |
|
|
|
Returns |
|
------- |
|
map_coordinates : ndarray |
|
The result of transforming the input. The shape of the output is |
|
derived from that of `coordinates` by dropping the first axis. |
|
|
|
See Also |
|
-------- |
|
spline_filter, geometric_transform, scipy.interpolate |
|
|
|
Notes |
|
----- |
|
For complex-valued `input`, this function maps the real and imaginary |
|
components independently. |
|
|
|
.. versionadded:: 1.6.0 |
|
Complex-valued support added. |
|
|
|
Examples |
|
-------- |
|
>>> from scipy import ndimage |
|
>>> import numpy as np |
|
>>> a = np.arange(12.).reshape((4, 3)) |
|
>>> a |
|
array([[ 0., 1., 2.], |
|
[ 3., 4., 5.], |
|
[ 6., 7., 8.], |
|
[ 9., 10., 11.]]) |
|
>>> ndimage.map_coordinates(a, [[0.5, 2], [0.5, 1]], order=1) |
|
array([ 2., 7.]) |
|
|
|
Above, the interpolated value of a[0.5, 0.5] gives output[0], while |
|
a[2, 1] is output[1]. |
|
|
|
>>> inds = np.array([[0.5, 2], [0.5, 4]]) |
|
>>> ndimage.map_coordinates(a, inds, order=1, cval=-33.3) |
|
array([ 2. , -33.3]) |
|
>>> ndimage.map_coordinates(a, inds, order=1, mode='nearest') |
|
array([ 2., 8.]) |
|
>>> ndimage.map_coordinates(a, inds, order=1, cval=0, output=bool) |
|
array([ True, False], dtype=bool) |
|
|
|
""" |
|
if order < 0 or order > 5: |
|
raise RuntimeError('spline order not supported') |
|
input = np.asarray(input) |
|
coordinates = np.asarray(coordinates) |
|
if np.iscomplexobj(coordinates): |
|
raise TypeError('Complex type not supported') |
|
output_shape = coordinates.shape[1:] |
|
if input.ndim < 1 or len(output_shape) < 1: |
|
raise RuntimeError('input and output rank must be > 0') |
|
if coordinates.shape[0] != input.ndim: |
|
raise RuntimeError('invalid shape for coordinate array') |
|
complex_output = np.iscomplexobj(input) |
|
output = _ni_support._get_output(output, input, shape=output_shape, |
|
complex_output=complex_output) |
|
if complex_output: |
|
kwargs = dict(order=order, mode=mode, prefilter=prefilter) |
|
map_coordinates(input.real, coordinates, output=output.real, |
|
cval=np.real(cval), **kwargs) |
|
map_coordinates(input.imag, coordinates, 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 |
|
mode = _ni_support._extend_mode_to_code(mode) |
|
_nd_image.geometric_transform(filtered, None, coordinates, None, None, |
|
output, order, mode, cval, npad, None, None) |
|
return output |
|
|
|
|
|
@docfiller |
|
def affine_transform(input, matrix, offset=0.0, output_shape=None, |
|
output=None, order=3, |
|
mode='constant', cval=0.0, prefilter=True): |
|
""" |
|
Apply an affine transformation. |
|
|
|
Given an output image pixel index vector ``o``, the pixel value |
|
is determined from the input image at position |
|
``np.dot(matrix, o) + offset``. |
|
|
|
This does 'pull' (or 'backward') resampling, transforming the output space |
|
to the input to locate data. Affine transformations are often described in |
|
the 'push' (or 'forward') direction, transforming input to output. If you |
|
have a matrix for the 'push' transformation, use its inverse |
|
(:func:`numpy.linalg.inv`) in this function. |
|
|
|
Parameters |
|
---------- |
|
%(input)s |
|
matrix : ndarray |
|
The inverse coordinate transformation matrix, mapping output |
|
coordinates to input coordinates. If ``ndim`` is the number of |
|
dimensions of ``input``, the given matrix must have one of the |
|
following shapes: |
|
|
|
- ``(ndim, ndim)``: the linear transformation matrix for each |
|
output coordinate. |
|
- ``(ndim,)``: assume that the 2-D transformation matrix is |
|
diagonal, with the diagonal specified by the given value. A more |
|
efficient algorithm is then used that exploits the separability |
|
of the problem. |
|
- ``(ndim + 1, ndim + 1)``: assume that the transformation is |
|
specified using homogeneous coordinates [1]_. In this case, any |
|
value passed to ``offset`` is ignored. |
|
- ``(ndim, ndim + 1)``: as above, but the bottom row of a |
|
homogeneous transformation matrix is always ``[0, 0, ..., 1]``, |
|
and may be omitted. |
|
|
|
offset : float or sequence, optional |
|
The offset into the array where the transform is applied. If a float, |
|
`offset` is the same for each axis. If a sequence, `offset` should |
|
contain one value for each axis. |
|
output_shape : tuple of ints, optional |
|
Shape tuple. |
|
%(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 |
|
|
|
Returns |
|
------- |
|
affine_transform : ndarray |
|
The transformed input. |
|
|
|
Notes |
|
----- |
|
The given matrix and offset are used to find for each point in the |
|
output the corresponding coordinates in the input by an affine |
|
transformation. The value of the input at those coordinates is |
|
determined by spline interpolation of the requested order. Points |
|
outside the boundaries of the input are filled according to the given |
|
mode. |
|
|
|
.. versionchanged:: 0.18.0 |
|
Previously, the exact interpretation of the affine transformation |
|
depended on whether the matrix was supplied as a 1-D or a |
|
2-D array. If a 1-D array was supplied |
|
to the matrix parameter, the output pixel value at index ``o`` |
|
was determined from the input image at position |
|
``matrix * (o + offset)``. |
|
|
|
For complex-valued `input`, this function transforms the real and imaginary |
|
components independently. |
|
|
|
.. versionadded:: 1.6.0 |
|
Complex-valued support added. |
|
|
|
References |
|
---------- |
|
.. [1] https://en.wikipedia.org/wiki/Homogeneous_coordinates |
|
""" |
|
if order < 0 or order > 5: |
|
raise RuntimeError('spline order not supported') |
|
input = np.asarray(input) |
|
if output_shape is None: |
|
if isinstance(output, np.ndarray): |
|
output_shape = output.shape |
|
else: |
|
output_shape = input.shape |
|
if input.ndim < 1 or len(output_shape) < 1: |
|
raise RuntimeError('input and output rank must be > 0') |
|
complex_output = np.iscomplexobj(input) |
|
output = _ni_support._get_output(output, input, shape=output_shape, |
|
complex_output=complex_output) |
|
if complex_output: |
|
kwargs = dict(offset=offset, output_shape=output_shape, order=order, |
|
mode=mode, prefilter=prefilter) |
|
affine_transform(input.real, matrix, output=output.real, |
|
cval=np.real(cval), **kwargs) |
|
affine_transform(input.imag, matrix, 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 |
|
mode = _ni_support._extend_mode_to_code(mode) |
|
matrix = np.asarray(matrix, dtype=np.float64) |
|
if matrix.ndim not in [1, 2] or matrix.shape[0] < 1: |
|
raise RuntimeError('no proper affine matrix provided') |
|
if (matrix.ndim == 2 and matrix.shape[1] == input.ndim + 1 and |
|
(matrix.shape[0] in [input.ndim, input.ndim + 1])): |
|
if matrix.shape[0] == input.ndim + 1: |
|
exptd = [0] * input.ndim + [1] |
|
if not np.all(matrix[input.ndim] == exptd): |
|
msg = (f'Expected homogeneous transformation matrix with ' |
|
f'shape {matrix.shape} for image shape {input.shape}, ' |
|
f'but bottom row was not equal to {exptd}') |
|
raise ValueError(msg) |
|
|
|
offset = matrix[:input.ndim, input.ndim] |
|
matrix = matrix[:input.ndim, :input.ndim] |
|
if matrix.shape[0] != input.ndim: |
|
raise RuntimeError('affine matrix has wrong number of rows') |
|
if matrix.ndim == 2 and matrix.shape[1] != output.ndim: |
|
raise RuntimeError('affine matrix has wrong number of columns') |
|
if not matrix.flags.contiguous: |
|
matrix = matrix.copy() |
|
offset = _ni_support._normalize_sequence(offset, input.ndim) |
|
offset = np.asarray(offset, dtype=np.float64) |
|
if offset.ndim != 1 or offset.shape[0] < 1: |
|
raise RuntimeError('no proper offset provided') |
|
if not offset.flags.contiguous: |
|
offset = offset.copy() |
|
if matrix.ndim == 1: |
|
warnings.warn( |
|
"The behavior of affine_transform with a 1-D " |
|
"array supplied for the matrix parameter has changed in " |
|
"SciPy 0.18.0.", |
|
stacklevel=2 |
|
) |
|
_nd_image.zoom_shift(filtered, matrix, offset/matrix, output, order, |
|
mode, cval, npad, False) |
|
else: |
|
_nd_image.geometric_transform(filtered, None, None, matrix, offset, |
|
output, order, mode, cval, npad, None, |
|
None) |
|
return output |
|
|
|
|
|
@docfiller |
|
def shift(input, shift, output=None, order=3, mode='constant', cval=0.0, |
|
prefilter=True): |
|
""" |
|
Shift an array. |
|
|
|
The array is shifted using spline interpolation of the requested order. |
|
Points outside the boundaries of the input are filled according to the |
|
given mode. |
|
|
|
Parameters |
|
---------- |
|
%(input)s |
|
shift : float or sequence |
|
The shift along the axes. If a float, `shift` is the same for each |
|
axis. If a sequence, `shift` 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 |
|
|
|
Returns |
|
------- |
|
shift : ndarray |
|
The shifted input. |
|
|
|
See Also |
|
-------- |
|
affine_transform : Affine transformations |
|
|
|
Notes |
|
----- |
|
For complex-valued `input`, this function shifts the real and imaginary |
|
components independently. |
|
|
|
.. versionadded:: 1.6.0 |
|
Complex-valued support added. |
|
|
|
Examples |
|
-------- |
|
Import the necessary modules and an exemplary image. |
|
|
|
>>> from scipy.ndimage import shift |
|
>>> import matplotlib.pyplot as plt |
|
>>> from scipy import datasets |
|
>>> image = datasets.ascent() |
|
|
|
Shift the image vertically by 20 pixels. |
|
|
|
>>> image_shifted_vertically = shift(image, (20, 0)) |
|
|
|
Shift the image vertically by -200 pixels and horizontally by 100 pixels. |
|
|
|
>>> image_shifted_both_directions = shift(image, (-200, 100)) |
|
|
|
Plot the original and the shifted images. |
|
|
|
>>> fig, axes = plt.subplots(3, 1, figsize=(4, 12)) |
|
>>> plt.gray() # show the filtered result in grayscale |
|
>>> top, middle, bottom = axes |
|
>>> for ax in axes: |
|
... ax.set_axis_off() # remove coordinate system |
|
>>> top.imshow(image) |
|
>>> top.set_title("Original image") |
|
>>> middle.imshow(image_shifted_vertically) |
|
>>> middle.set_title("Vertically shifted image") |
|
>>> bottom.imshow(image_shifted_both_directions) |
|
>>> bottom.set_title("Image shifted in both directions") |
|
>>> fig.tight_layout() |
|
""" |
|
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') |
|
complex_output = np.iscomplexobj(input) |
|
output = _ni_support._get_output(output, input, complex_output=complex_output) |
|
if complex_output: |
|
|
|
from scipy.ndimage._interpolation import shift as _shift |
|
|
|
kwargs = dict(order=order, mode=mode, prefilter=prefilter) |
|
_shift(input.real, shift, output=output.real, cval=np.real(cval), **kwargs) |
|
_shift(input.imag, shift, 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 |
|
mode = _ni_support._extend_mode_to_code(mode) |
|
shift = _ni_support._normalize_sequence(shift, input.ndim) |
|
shift = [-ii for ii in shift] |
|
shift = np.asarray(shift, dtype=np.float64) |
|
if not shift.flags.contiguous: |
|
shift = shift.copy() |
|
_nd_image.zoom_shift(filtered, None, shift, output, order, mode, cval, |
|
npad, False) |
|
return output |
|
|
|
|
|
@docfiller |
|
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: |
|
|
|
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: |
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
@docfiller |
|
def rotate(input, angle, axes=(1, 0), reshape=True, output=None, order=3, |
|
mode='constant', cval=0.0, prefilter=True): |
|
""" |
|
Rotate an array. |
|
|
|
The array is rotated in the plane defined by the two axes given by the |
|
`axes` parameter using spline interpolation of the requested order. |
|
|
|
Parameters |
|
---------- |
|
%(input)s |
|
angle : float |
|
The rotation angle in degrees. |
|
axes : tuple of 2 ints, optional |
|
The two axes that define the plane of rotation. Default is the first |
|
two axes. |
|
reshape : bool, optional |
|
If `reshape` is true, the output shape is adapted so that the input |
|
array is contained completely in the output. Default is True. |
|
%(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 |
|
|
|
Returns |
|
------- |
|
rotate : ndarray |
|
The rotated input. |
|
|
|
Notes |
|
----- |
|
For complex-valued `input`, this function rotates 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(figsize=(10, 3)) |
|
>>> ax1, ax2, ax3 = fig.subplots(1, 3) |
|
>>> img = datasets.ascent() |
|
>>> img_45 = ndimage.rotate(img, 45, reshape=False) |
|
>>> full_img_45 = ndimage.rotate(img, 45, reshape=True) |
|
>>> ax1.imshow(img, cmap='gray') |
|
>>> ax1.set_axis_off() |
|
>>> ax2.imshow(img_45, cmap='gray') |
|
>>> ax2.set_axis_off() |
|
>>> ax3.imshow(full_img_45, cmap='gray') |
|
>>> ax3.set_axis_off() |
|
>>> fig.set_layout_engine('tight') |
|
>>> plt.show() |
|
>>> print(img.shape) |
|
(512, 512) |
|
>>> print(img_45.shape) |
|
(512, 512) |
|
>>> print(full_img_45.shape) |
|
(724, 724) |
|
|
|
""" |
|
input_arr = np.asarray(input) |
|
ndim = input_arr.ndim |
|
|
|
if ndim < 2: |
|
raise ValueError('input array should be at least 2D') |
|
|
|
axes = list(axes) |
|
|
|
if len(axes) != 2: |
|
raise ValueError('axes should contain exactly two values') |
|
|
|
if not all([float(ax).is_integer() for ax in axes]): |
|
raise ValueError('axes should contain only integer values') |
|
|
|
if axes[0] < 0: |
|
axes[0] += ndim |
|
if axes[1] < 0: |
|
axes[1] += ndim |
|
if axes[0] < 0 or axes[1] < 0 or axes[0] >= ndim or axes[1] >= ndim: |
|
raise ValueError('invalid rotation plane specified') |
|
|
|
axes.sort() |
|
|
|
c, s = special.cosdg(angle), special.sindg(angle) |
|
|
|
rot_matrix = np.array([[c, s], |
|
[-s, c]]) |
|
|
|
img_shape = np.asarray(input_arr.shape) |
|
in_plane_shape = img_shape[axes] |
|
if reshape: |
|
|
|
iy, ix = in_plane_shape |
|
out_bounds = rot_matrix @ [[0, 0, iy, iy], |
|
[0, ix, 0, ix]] |
|
|
|
out_plane_shape = (np.ptp(out_bounds, axis=1) + 0.5).astype(int) |
|
else: |
|
out_plane_shape = img_shape[axes] |
|
|
|
out_center = rot_matrix @ ((out_plane_shape - 1) / 2) |
|
in_center = (in_plane_shape - 1) / 2 |
|
offset = in_center - out_center |
|
|
|
output_shape = img_shape |
|
output_shape[axes] = out_plane_shape |
|
output_shape = tuple(output_shape) |
|
|
|
complex_output = np.iscomplexobj(input_arr) |
|
output = _ni_support._get_output(output, input_arr, shape=output_shape, |
|
complex_output=complex_output) |
|
|
|
if ndim <= 2: |
|
affine_transform(input_arr, rot_matrix, offset, output_shape, output, |
|
order, mode, cval, prefilter) |
|
else: |
|
|
|
|
|
planes_coord = itertools.product( |
|
*[[slice(None)] if ax in axes else range(img_shape[ax]) |
|
for ax in range(ndim)]) |
|
|
|
out_plane_shape = tuple(out_plane_shape) |
|
|
|
for coordinates in planes_coord: |
|
ia = input_arr[coordinates] |
|
oa = output[coordinates] |
|
affine_transform(ia, rot_matrix, offset, out_plane_shape, |
|
oa, order, mode, cval, prefilter) |
|
|
|
return output |
|
|