Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/ndimage/_interpolation.py: 9%
268 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
1# Copyright (C) 2003-2005 Peter J. Verveer
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6#
7# 1. Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#
10# 2. Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following
12# disclaimer in the documentation and/or other materials provided
13# with the distribution.
14#
15# 3. The name of the author may not be used to endorse or promote
16# products derived from this software without specific prior
17# written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31import itertools
32import warnings
34import numpy
35from numpy.core.multiarray import normalize_axis_index
37from scipy import special
38from . import _ni_support
39from . import _nd_image
40from ._ni_docstrings import docfiller
43__all__ = ['spline_filter1d', 'spline_filter', 'geometric_transform',
44 'map_coordinates', 'affine_transform', 'shift', 'zoom', 'rotate']
47@docfiller
48def spline_filter1d(input, order=3, axis=-1, output=numpy.float64,
49 mode='mirror'):
50 """
51 Calculate a 1-D spline filter along the given axis.
53 The lines of the array along the given axis are filtered by a
54 spline filter. The order of the spline must be >= 2 and <= 5.
56 Parameters
57 ----------
58 %(input)s
59 order : int, optional
60 The order of the spline, default is 3.
61 axis : int, optional
62 The axis along which the spline filter is applied. Default is the last
63 axis.
64 output : ndarray or dtype, optional
65 The array in which to place the output, or the dtype of the returned
66 array. Default is ``numpy.float64``.
67 %(mode_interp_mirror)s
69 Returns
70 -------
71 spline_filter1d : ndarray
72 The filtered input.
74 Notes
75 -----
76 All of the interpolation functions in `ndimage` do spline interpolation of
77 the input image. If using B-splines of `order > 1`, the input image
78 values have to be converted to B-spline coefficients first, which is
79 done by applying this 1-D filter sequentially along all
80 axes of the input. All functions that require B-spline coefficients
81 will automatically filter their inputs, a behavior controllable with
82 the `prefilter` keyword argument. For functions that accept a `mode`
83 parameter, the result will only be correct if it matches the `mode`
84 used when filtering.
86 For complex-valued `input`, this function processes the real and imaginary
87 components independently.
89 .. versionadded:: 1.6.0
90 Complex-valued support added.
92 See Also
93 --------
94 spline_filter : Multidimensional spline filter.
96 Examples
97 --------
98 We can filter an image using 1-D spline along the given axis:
100 >>> from scipy.ndimage import spline_filter1d
101 >>> import numpy as np
102 >>> import matplotlib.pyplot as plt
103 >>> orig_img = np.eye(20) # create an image
104 >>> orig_img[10, :] = 1.0
105 >>> sp_filter_axis_0 = spline_filter1d(orig_img, axis=0)
106 >>> sp_filter_axis_1 = spline_filter1d(orig_img, axis=1)
107 >>> f, ax = plt.subplots(1, 3, sharex=True)
108 >>> for ind, data in enumerate([[orig_img, "original image"],
109 ... [sp_filter_axis_0, "spline filter (axis=0)"],
110 ... [sp_filter_axis_1, "spline filter (axis=1)"]]):
111 ... ax[ind].imshow(data[0], cmap='gray_r')
112 ... ax[ind].set_title(data[1])
113 >>> plt.tight_layout()
114 >>> plt.show()
116 """
117 if order < 0 or order > 5:
118 raise RuntimeError('spline order not supported')
119 input = numpy.asarray(input)
120 complex_output = numpy.iscomplexobj(input)
121 output = _ni_support._get_output(output, input,
122 complex_output=complex_output)
123 if complex_output:
124 spline_filter1d(input.real, order, axis, output.real, mode)
125 spline_filter1d(input.imag, order, axis, output.imag, mode)
126 return output
127 if order in [0, 1]:
128 output[...] = numpy.array(input)
129 else:
130 mode = _ni_support._extend_mode_to_code(mode)
131 axis = normalize_axis_index(axis, input.ndim)
132 _nd_image.spline_filter1d(input, order, axis, output, mode)
133 return output
136def spline_filter(input, order=3, output=numpy.float64, mode='mirror'):
137 """
138 Multidimensional spline filter.
140 For more details, see `spline_filter1d`.
142 See Also
143 --------
144 spline_filter1d : Calculate a 1-D spline filter along the given axis.
146 Notes
147 -----
148 The multidimensional filter is implemented as a sequence of
149 1-D spline filters. The intermediate arrays are stored
150 in the same data type as the output. Therefore, for output types
151 with a limited precision, the results may be imprecise because
152 intermediate results may be stored with insufficient precision.
154 For complex-valued `input`, this function processes the real and imaginary
155 components independently.
157 .. versionadded:: 1.6.0
158 Complex-valued support added.
160 Examples
161 --------
162 We can filter an image using multidimentional splines:
164 >>> from scipy.ndimage import spline_filter
165 >>> import numpy as np
166 >>> import matplotlib.pyplot as plt
167 >>> orig_img = np.eye(20) # create an image
168 >>> orig_img[10, :] = 1.0
169 >>> sp_filter = spline_filter(orig_img, order=3)
170 >>> f, ax = plt.subplots(1, 2, sharex=True)
171 >>> for ind, data in enumerate([[orig_img, "original image"],
172 ... [sp_filter, "spline filter"]]):
173 ... ax[ind].imshow(data[0], cmap='gray_r')
174 ... ax[ind].set_title(data[1])
175 >>> plt.tight_layout()
176 >>> plt.show()
178 """
179 if order < 2 or order > 5:
180 raise RuntimeError('spline order not supported')
181 input = numpy.asarray(input)
182 complex_output = numpy.iscomplexobj(input)
183 output = _ni_support._get_output(output, input,
184 complex_output=complex_output)
185 if complex_output:
186 spline_filter(input.real, order, output.real, mode)
187 spline_filter(input.imag, order, output.imag, mode)
188 return output
189 if order not in [0, 1] and input.ndim > 0:
190 for axis in range(input.ndim):
191 spline_filter1d(input, order, axis, output=output, mode=mode)
192 input = output
193 else:
194 output[...] = input[...]
195 return output
198def _prepad_for_spline_filter(input, mode, cval):
199 if mode in ['nearest', 'grid-constant']:
200 npad = 12
201 if mode == 'grid-constant':
202 padded = numpy.pad(input, npad, mode='constant',
203 constant_values=cval)
204 elif mode == 'nearest':
205 padded = numpy.pad(input, npad, mode='edge')
206 else:
207 # other modes have exact boundary conditions implemented so
208 # no prepadding is needed
209 npad = 0
210 padded = input
211 return padded, npad
214@docfiller
215def geometric_transform(input, mapping, output_shape=None,
216 output=None, order=3,
217 mode='constant', cval=0.0, prefilter=True,
218 extra_arguments=(), extra_keywords={}):
219 """
220 Apply an arbitrary geometric transform.
222 The given mapping function is used to find, for each point in the
223 output, the corresponding coordinates in the input. The value of the
224 input at those coordinates is determined by spline interpolation of
225 the requested order.
227 Parameters
228 ----------
229 %(input)s
230 mapping : {callable, scipy.LowLevelCallable}
231 A callable object that accepts a tuple of length equal to the output
232 array rank, and returns the corresponding input coordinates as a tuple
233 of length equal to the input array rank.
234 output_shape : tuple of ints, optional
235 Shape tuple.
236 %(output)s
237 order : int, optional
238 The order of the spline interpolation, default is 3.
239 The order has to be in the range 0-5.
240 %(mode_interp_constant)s
241 %(cval)s
242 %(prefilter)s
243 extra_arguments : tuple, optional
244 Extra arguments passed to `mapping`.
245 extra_keywords : dict, optional
246 Extra keywords passed to `mapping`.
248 Returns
249 -------
250 output : ndarray
251 The filtered input.
253 See Also
254 --------
255 map_coordinates, affine_transform, spline_filter1d
258 Notes
259 -----
260 This function also accepts low-level callback functions with one
261 the following signatures and wrapped in `scipy.LowLevelCallable`:
263 .. code:: c
265 int mapping(npy_intp *output_coordinates, double *input_coordinates,
266 int output_rank, int input_rank, void *user_data)
267 int mapping(intptr_t *output_coordinates, double *input_coordinates,
268 int output_rank, int input_rank, void *user_data)
270 The calling function iterates over the elements of the output array,
271 calling the callback function at each element. The coordinates of the
272 current output element are passed through ``output_coordinates``. The
273 callback function must return the coordinates at which the input must
274 be interpolated in ``input_coordinates``. The rank of the input and
275 output arrays are given by ``input_rank`` and ``output_rank``
276 respectively. ``user_data`` is the data pointer provided
277 to `scipy.LowLevelCallable` as-is.
279 The callback function must return an integer error status that is zero
280 if something went wrong and one otherwise. If an error occurs, you should
281 normally set the Python error status with an informative message
282 before returning, otherwise a default error message is set by the
283 calling function.
285 In addition, some other low-level function pointer specifications
286 are accepted, but these are for backward compatibility only and should
287 not be used in new code.
289 For complex-valued `input`, this function transforms the real and imaginary
290 components independently.
292 .. versionadded:: 1.6.0
293 Complex-valued support added.
295 Examples
296 --------
297 >>> import numpy as np
298 >>> from scipy.ndimage import geometric_transform
299 >>> a = np.arange(12.).reshape((4, 3))
300 >>> def shift_func(output_coords):
301 ... return (output_coords[0] - 0.5, output_coords[1] - 0.5)
302 ...
303 >>> geometric_transform(a, shift_func)
304 array([[ 0. , 0. , 0. ],
305 [ 0. , 1.362, 2.738],
306 [ 0. , 4.812, 6.187],
307 [ 0. , 8.263, 9.637]])
309 >>> b = [1, 2, 3, 4, 5]
310 >>> def shift_func(output_coords):
311 ... return (output_coords[0] - 3,)
312 ...
313 >>> geometric_transform(b, shift_func, mode='constant')
314 array([0, 0, 0, 1, 2])
315 >>> geometric_transform(b, shift_func, mode='nearest')
316 array([1, 1, 1, 1, 2])
317 >>> geometric_transform(b, shift_func, mode='reflect')
318 array([3, 2, 1, 1, 2])
319 >>> geometric_transform(b, shift_func, mode='wrap')
320 array([2, 3, 4, 1, 2])
322 """
323 if order < 0 or order > 5:
324 raise RuntimeError('spline order not supported')
325 input = numpy.asarray(input)
326 if output_shape is None:
327 output_shape = input.shape
328 if input.ndim < 1 or len(output_shape) < 1:
329 raise RuntimeError('input and output rank must be > 0')
330 complex_output = numpy.iscomplexobj(input)
331 output = _ni_support._get_output(output, input, shape=output_shape,
332 complex_output=complex_output)
333 if complex_output:
334 kwargs = dict(order=order, mode=mode, prefilter=prefilter,
335 output_shape=output_shape,
336 extra_arguments=extra_arguments,
337 extra_keywords=extra_keywords)
338 geometric_transform(input.real, mapping, output=output.real,
339 cval=numpy.real(cval), **kwargs)
340 geometric_transform(input.imag, mapping, output=output.imag,
341 cval=numpy.imag(cval), **kwargs)
342 return output
344 if prefilter and order > 1:
345 padded, npad = _prepad_for_spline_filter(input, mode, cval)
346 filtered = spline_filter(padded, order, output=numpy.float64,
347 mode=mode)
348 else:
349 npad = 0
350 filtered = input
351 mode = _ni_support._extend_mode_to_code(mode)
352 _nd_image.geometric_transform(filtered, mapping, None, None, None, output,
353 order, mode, cval, npad, extra_arguments,
354 extra_keywords)
355 return output
358@docfiller
359def map_coordinates(input, coordinates, output=None, order=3,
360 mode='constant', cval=0.0, prefilter=True):
361 """
362 Map the input array to new coordinates by interpolation.
364 The array of coordinates is used to find, for each point in the output,
365 the corresponding coordinates in the input. The value of the input at
366 those coordinates is determined by spline interpolation of the
367 requested order.
369 The shape of the output is derived from that of the coordinate
370 array by dropping the first axis. The values of the array along
371 the first axis are the coordinates in the input array at which the
372 output value is found.
374 Parameters
375 ----------
376 %(input)s
377 coordinates : array_like
378 The coordinates at which `input` is evaluated.
379 %(output)s
380 order : int, optional
381 The order of the spline interpolation, default is 3.
382 The order has to be in the range 0-5.
383 %(mode_interp_constant)s
384 %(cval)s
385 %(prefilter)s
387 Returns
388 -------
389 map_coordinates : ndarray
390 The result of transforming the input. The shape of the output is
391 derived from that of `coordinates` by dropping the first axis.
393 See Also
394 --------
395 spline_filter, geometric_transform, scipy.interpolate
397 Notes
398 -----
399 For complex-valued `input`, this function maps the real and imaginary
400 components independently.
402 .. versionadded:: 1.6.0
403 Complex-valued support added.
405 Examples
406 --------
407 >>> from scipy import ndimage
408 >>> import numpy as np
409 >>> a = np.arange(12.).reshape((4, 3))
410 >>> a
411 array([[ 0., 1., 2.],
412 [ 3., 4., 5.],
413 [ 6., 7., 8.],
414 [ 9., 10., 11.]])
415 >>> ndimage.map_coordinates(a, [[0.5, 2], [0.5, 1]], order=1)
416 array([ 2., 7.])
418 Above, the interpolated value of a[0.5, 0.5] gives output[0], while
419 a[2, 1] is output[1].
421 >>> inds = np.array([[0.5, 2], [0.5, 4]])
422 >>> ndimage.map_coordinates(a, inds, order=1, cval=-33.3)
423 array([ 2. , -33.3])
424 >>> ndimage.map_coordinates(a, inds, order=1, mode='nearest')
425 array([ 2., 8.])
426 >>> ndimage.map_coordinates(a, inds, order=1, cval=0, output=bool)
427 array([ True, False], dtype=bool)
429 """
430 if order < 0 or order > 5:
431 raise RuntimeError('spline order not supported')
432 input = numpy.asarray(input)
433 coordinates = numpy.asarray(coordinates)
434 if numpy.iscomplexobj(coordinates):
435 raise TypeError('Complex type not supported')
436 output_shape = coordinates.shape[1:]
437 if input.ndim < 1 or len(output_shape) < 1:
438 raise RuntimeError('input and output rank must be > 0')
439 if coordinates.shape[0] != input.ndim:
440 raise RuntimeError('invalid shape for coordinate array')
441 complex_output = numpy.iscomplexobj(input)
442 output = _ni_support._get_output(output, input, shape=output_shape,
443 complex_output=complex_output)
444 if complex_output:
445 kwargs = dict(order=order, mode=mode, prefilter=prefilter)
446 map_coordinates(input.real, coordinates, output=output.real,
447 cval=numpy.real(cval), **kwargs)
448 map_coordinates(input.imag, coordinates, output=output.imag,
449 cval=numpy.imag(cval), **kwargs)
450 return output
451 if prefilter and order > 1:
452 padded, npad = _prepad_for_spline_filter(input, mode, cval)
453 filtered = spline_filter(padded, order, output=numpy.float64,
454 mode=mode)
455 else:
456 npad = 0
457 filtered = input
458 mode = _ni_support._extend_mode_to_code(mode)
459 _nd_image.geometric_transform(filtered, None, coordinates, None, None,
460 output, order, mode, cval, npad, None, None)
461 return output
464@docfiller
465def affine_transform(input, matrix, offset=0.0, output_shape=None,
466 output=None, order=3,
467 mode='constant', cval=0.0, prefilter=True):
468 """
469 Apply an affine transformation.
471 Given an output image pixel index vector ``o``, the pixel value
472 is determined from the input image at position
473 ``np.dot(matrix, o) + offset``.
475 This does 'pull' (or 'backward') resampling, transforming the output space
476 to the input to locate data. Affine transformations are often described in
477 the 'push' (or 'forward') direction, transforming input to output. If you
478 have a matrix for the 'push' transformation, use its inverse
479 (:func:`numpy.linalg.inv`) in this function.
481 Parameters
482 ----------
483 %(input)s
484 matrix : ndarray
485 The inverse coordinate transformation matrix, mapping output
486 coordinates to input coordinates. If ``ndim`` is the number of
487 dimensions of ``input``, the given matrix must have one of the
488 following shapes:
490 - ``(ndim, ndim)``: the linear transformation matrix for each
491 output coordinate.
492 - ``(ndim,)``: assume that the 2-D transformation matrix is
493 diagonal, with the diagonal specified by the given value. A more
494 efficient algorithm is then used that exploits the separability
495 of the problem.
496 - ``(ndim + 1, ndim + 1)``: assume that the transformation is
497 specified using homogeneous coordinates [1]_. In this case, any
498 value passed to ``offset`` is ignored.
499 - ``(ndim, ndim + 1)``: as above, but the bottom row of a
500 homogeneous transformation matrix is always ``[0, 0, ..., 1]``,
501 and may be omitted.
503 offset : float or sequence, optional
504 The offset into the array where the transform is applied. If a float,
505 `offset` is the same for each axis. If a sequence, `offset` should
506 contain one value for each axis.
507 output_shape : tuple of ints, optional
508 Shape tuple.
509 %(output)s
510 order : int, optional
511 The order of the spline interpolation, default is 3.
512 The order has to be in the range 0-5.
513 %(mode_interp_constant)s
514 %(cval)s
515 %(prefilter)s
517 Returns
518 -------
519 affine_transform : ndarray
520 The transformed input.
522 Notes
523 -----
524 The given matrix and offset are used to find for each point in the
525 output the corresponding coordinates in the input by an affine
526 transformation. The value of the input at those coordinates is
527 determined by spline interpolation of the requested order. Points
528 outside the boundaries of the input are filled according to the given
529 mode.
531 .. versionchanged:: 0.18.0
532 Previously, the exact interpretation of the affine transformation
533 depended on whether the matrix was supplied as a 1-D or a
534 2-D array. If a 1-D array was supplied
535 to the matrix parameter, the output pixel value at index ``o``
536 was determined from the input image at position
537 ``matrix * (o + offset)``.
539 For complex-valued `input`, this function transforms the real and imaginary
540 components independently.
542 .. versionadded:: 1.6.0
543 Complex-valued support added.
545 References
546 ----------
547 .. [1] https://en.wikipedia.org/wiki/Homogeneous_coordinates
548 """
549 if order < 0 or order > 5:
550 raise RuntimeError('spline order not supported')
551 input = numpy.asarray(input)
552 if output_shape is None:
553 if isinstance(output, numpy.ndarray):
554 output_shape = output.shape
555 else:
556 output_shape = input.shape
557 if input.ndim < 1 or len(output_shape) < 1:
558 raise RuntimeError('input and output rank must be > 0')
559 complex_output = numpy.iscomplexobj(input)
560 output = _ni_support._get_output(output, input, shape=output_shape,
561 complex_output=complex_output)
562 if complex_output:
563 kwargs = dict(offset=offset, output_shape=output_shape, order=order,
564 mode=mode, prefilter=prefilter)
565 affine_transform(input.real, matrix, output=output.real,
566 cval=numpy.real(cval), **kwargs)
567 affine_transform(input.imag, matrix, output=output.imag,
568 cval=numpy.imag(cval), **kwargs)
569 return output
570 if prefilter and order > 1:
571 padded, npad = _prepad_for_spline_filter(input, mode, cval)
572 filtered = spline_filter(padded, order, output=numpy.float64,
573 mode=mode)
574 else:
575 npad = 0
576 filtered = input
577 mode = _ni_support._extend_mode_to_code(mode)
578 matrix = numpy.asarray(matrix, dtype=numpy.float64)
579 if matrix.ndim not in [1, 2] or matrix.shape[0] < 1:
580 raise RuntimeError('no proper affine matrix provided')
581 if (matrix.ndim == 2 and matrix.shape[1] == input.ndim + 1 and
582 (matrix.shape[0] in [input.ndim, input.ndim + 1])):
583 if matrix.shape[0] == input.ndim + 1:
584 exptd = [0] * input.ndim + [1]
585 if not numpy.all(matrix[input.ndim] == exptd):
586 msg = ('Expected homogeneous transformation matrix with '
587 'shape %s for image shape %s, but bottom row was '
588 'not equal to %s' % (matrix.shape, input.shape, exptd))
589 raise ValueError(msg)
590 # assume input is homogeneous coordinate transformation matrix
591 offset = matrix[:input.ndim, input.ndim]
592 matrix = matrix[:input.ndim, :input.ndim]
593 if matrix.shape[0] != input.ndim:
594 raise RuntimeError('affine matrix has wrong number of rows')
595 if matrix.ndim == 2 and matrix.shape[1] != output.ndim:
596 raise RuntimeError('affine matrix has wrong number of columns')
597 if not matrix.flags.contiguous:
598 matrix = matrix.copy()
599 offset = _ni_support._normalize_sequence(offset, input.ndim)
600 offset = numpy.asarray(offset, dtype=numpy.float64)
601 if offset.ndim != 1 or offset.shape[0] < 1:
602 raise RuntimeError('no proper offset provided')
603 if not offset.flags.contiguous:
604 offset = offset.copy()
605 if matrix.ndim == 1:
606 warnings.warn(
607 "The behavior of affine_transform with a 1-D "
608 "array supplied for the matrix parameter has changed in "
609 "SciPy 0.18.0."
610 )
611 _nd_image.zoom_shift(filtered, matrix, offset/matrix, output, order,
612 mode, cval, npad, False)
613 else:
614 _nd_image.geometric_transform(filtered, None, None, matrix, offset,
615 output, order, mode, cval, npad, None,
616 None)
617 return output
620@docfiller
621def shift(input, shift, output=None, order=3, mode='constant', cval=0.0,
622 prefilter=True):
623 """
624 Shift an array.
626 The array is shifted using spline interpolation of the requested order.
627 Points outside the boundaries of the input are filled according to the
628 given mode.
630 Parameters
631 ----------
632 %(input)s
633 shift : float or sequence
634 The shift along the axes. If a float, `shift` is the same for each
635 axis. If a sequence, `shift` should contain one value for each axis.
636 %(output)s
637 order : int, optional
638 The order of the spline interpolation, default is 3.
639 The order has to be in the range 0-5.
640 %(mode_interp_constant)s
641 %(cval)s
642 %(prefilter)s
644 Returns
645 -------
646 shift : ndarray
647 The shifted input.
649 Notes
650 -----
651 For complex-valued `input`, this function shifts the real and imaginary
652 components independently.
654 .. versionadded:: 1.6.0
655 Complex-valued support added.
657 """
658 if order < 0 or order > 5:
659 raise RuntimeError('spline order not supported')
660 input = numpy.asarray(input)
661 if input.ndim < 1:
662 raise RuntimeError('input and output rank must be > 0')
663 complex_output = numpy.iscomplexobj(input)
664 output = _ni_support._get_output(output, input,
665 complex_output=complex_output)
666 if complex_output:
667 # import under different name to avoid confusion with shift parameter
668 from scipy.ndimage._interpolation import shift as _shift
670 kwargs = dict(order=order, mode=mode, prefilter=prefilter)
671 _shift(input.real, shift, output=output.real, cval=numpy.real(cval),
672 **kwargs)
673 _shift(input.imag, shift, output=output.imag, cval=numpy.imag(cval),
674 **kwargs)
675 return output
676 if prefilter and order > 1:
677 padded, npad = _prepad_for_spline_filter(input, mode, cval)
678 filtered = spline_filter(padded, order, output=numpy.float64,
679 mode=mode)
680 else:
681 npad = 0
682 filtered = input
683 mode = _ni_support._extend_mode_to_code(mode)
684 shift = _ni_support._normalize_sequence(shift, input.ndim)
685 shift = [-ii for ii in shift]
686 shift = numpy.asarray(shift, dtype=numpy.float64)
687 if not shift.flags.contiguous:
688 shift = shift.copy()
689 _nd_image.zoom_shift(filtered, None, shift, output, order, mode, cval,
690 npad, False)
691 return output
694@docfiller
695def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0,
696 prefilter=True, *, grid_mode=False):
697 """
698 Zoom an array.
700 The array is zoomed using spline interpolation of the requested order.
702 Parameters
703 ----------
704 %(input)s
705 zoom : float or sequence
706 The zoom factor along the axes. If a float, `zoom` is the same for each
707 axis. If a sequence, `zoom` should contain one value for each axis.
708 %(output)s
709 order : int, optional
710 The order of the spline interpolation, default is 3.
711 The order has to be in the range 0-5.
712 %(mode_interp_constant)s
713 %(cval)s
714 %(prefilter)s
715 grid_mode : bool, optional
716 If False, the distance from the pixel centers is zoomed. Otherwise, the
717 distance including the full pixel extent is used. For example, a 1d
718 signal of length 5 is considered to have length 4 when `grid_mode` is
719 False, but length 5 when `grid_mode` is True. See the following
720 visual illustration:
722 .. code-block:: text
724 | pixel 1 | pixel 2 | pixel 3 | pixel 4 | pixel 5 |
725 |<-------------------------------------->|
726 vs.
727 |<----------------------------------------------->|
729 The starting point of the arrow in the diagram above corresponds to
730 coordinate location 0 in each mode.
732 Returns
733 -------
734 zoom : ndarray
735 The zoomed input.
737 Notes
738 -----
739 For complex-valued `input`, this function zooms the real and imaginary
740 components independently.
742 .. versionadded:: 1.6.0
743 Complex-valued support added.
745 Examples
746 --------
747 >>> from scipy import ndimage, datasets
748 >>> import matplotlib.pyplot as plt
750 >>> fig = plt.figure()
751 >>> ax1 = fig.add_subplot(121) # left side
752 >>> ax2 = fig.add_subplot(122) # right side
753 >>> ascent = datasets.ascent()
754 >>> result = ndimage.zoom(ascent, 3.0)
755 >>> ax1.imshow(ascent, vmin=0, vmax=255)
756 >>> ax2.imshow(result, vmin=0, vmax=255)
757 >>> plt.show()
759 >>> print(ascent.shape)
760 (512, 512)
762 >>> print(result.shape)
763 (1536, 1536)
764 """
765 if order < 0 or order > 5:
766 raise RuntimeError('spline order not supported')
767 input = numpy.asarray(input)
768 if input.ndim < 1:
769 raise RuntimeError('input and output rank must be > 0')
770 zoom = _ni_support._normalize_sequence(zoom, input.ndim)
771 output_shape = tuple(
772 [int(round(ii * jj)) for ii, jj in zip(input.shape, zoom)])
773 complex_output = numpy.iscomplexobj(input)
774 output = _ni_support._get_output(output, input, shape=output_shape,
775 complex_output=complex_output)
776 if complex_output:
777 # import under different name to avoid confusion with zoom parameter
778 from scipy.ndimage._interpolation import zoom as _zoom
780 kwargs = dict(order=order, mode=mode, prefilter=prefilter)
781 _zoom(input.real, zoom, output=output.real, cval=numpy.real(cval),
782 **kwargs)
783 _zoom(input.imag, zoom, output=output.imag, cval=numpy.imag(cval),
784 **kwargs)
785 return output
786 if prefilter and order > 1:
787 padded, npad = _prepad_for_spline_filter(input, mode, cval)
788 filtered = spline_filter(padded, order, output=numpy.float64,
789 mode=mode)
790 else:
791 npad = 0
792 filtered = input
793 if grid_mode:
794 # warn about modes that may have surprising behavior
795 suggest_mode = None
796 if mode == 'constant':
797 suggest_mode = 'grid-constant'
798 elif mode == 'wrap':
799 suggest_mode = 'grid-wrap'
800 if suggest_mode is not None:
801 warnings.warn(
802 ("It is recommended to use mode = {} instead of {} when "
803 "grid_mode is True.").format(suggest_mode, mode)
804 )
805 mode = _ni_support._extend_mode_to_code(mode)
807 zoom_div = numpy.array(output_shape)
808 zoom_nominator = numpy.array(input.shape)
809 if not grid_mode:
810 zoom_div -= 1
811 zoom_nominator -= 1
813 # Zooming to infinite values is unpredictable, so just choose
814 # zoom factor 1 instead
815 zoom = numpy.divide(zoom_nominator, zoom_div,
816 out=numpy.ones_like(input.shape, dtype=numpy.float64),
817 where=zoom_div != 0)
818 zoom = numpy.ascontiguousarray(zoom)
819 _nd_image.zoom_shift(filtered, zoom, None, output, order, mode, cval, npad,
820 grid_mode)
821 return output
824@docfiller
825def rotate(input, angle, axes=(1, 0), reshape=True, output=None, order=3,
826 mode='constant', cval=0.0, prefilter=True):
827 """
828 Rotate an array.
830 The array is rotated in the plane defined by the two axes given by the
831 `axes` parameter using spline interpolation of the requested order.
833 Parameters
834 ----------
835 %(input)s
836 angle : float
837 The rotation angle in degrees.
838 axes : tuple of 2 ints, optional
839 The two axes that define the plane of rotation. Default is the first
840 two axes.
841 reshape : bool, optional
842 If `reshape` is true, the output shape is adapted so that the input
843 array is contained completely in the output. Default is True.
844 %(output)s
845 order : int, optional
846 The order of the spline interpolation, default is 3.
847 The order has to be in the range 0-5.
848 %(mode_interp_constant)s
849 %(cval)s
850 %(prefilter)s
852 Returns
853 -------
854 rotate : ndarray
855 The rotated input.
857 Notes
858 -----
859 For complex-valued `input`, this function rotates the real and imaginary
860 components independently.
862 .. versionadded:: 1.6.0
863 Complex-valued support added.
865 Examples
866 --------
867 >>> from scipy import ndimage, datasets
868 >>> import matplotlib.pyplot as plt
869 >>> fig = plt.figure(figsize=(10, 3))
870 >>> ax1, ax2, ax3 = fig.subplots(1, 3)
871 >>> img = datasets.ascent()
872 >>> img_45 = ndimage.rotate(img, 45, reshape=False)
873 >>> full_img_45 = ndimage.rotate(img, 45, reshape=True)
874 >>> ax1.imshow(img, cmap='gray')
875 >>> ax1.set_axis_off()
876 >>> ax2.imshow(img_45, cmap='gray')
877 >>> ax2.set_axis_off()
878 >>> ax3.imshow(full_img_45, cmap='gray')
879 >>> ax3.set_axis_off()
880 >>> fig.set_layout_engine('tight')
881 >>> plt.show()
882 >>> print(img.shape)
883 (512, 512)
884 >>> print(img_45.shape)
885 (512, 512)
886 >>> print(full_img_45.shape)
887 (724, 724)
889 """
890 input_arr = numpy.asarray(input)
891 ndim = input_arr.ndim
893 if ndim < 2:
894 raise ValueError('input array should be at least 2D')
896 axes = list(axes)
898 if len(axes) != 2:
899 raise ValueError('axes should contain exactly two values')
901 if not all([float(ax).is_integer() for ax in axes]):
902 raise ValueError('axes should contain only integer values')
904 if axes[0] < 0:
905 axes[0] += ndim
906 if axes[1] < 0:
907 axes[1] += ndim
908 if axes[0] < 0 or axes[1] < 0 or axes[0] >= ndim or axes[1] >= ndim:
909 raise ValueError('invalid rotation plane specified')
911 axes.sort()
913 c, s = special.cosdg(angle), special.sindg(angle)
915 rot_matrix = numpy.array([[c, s],
916 [-s, c]])
918 img_shape = numpy.asarray(input_arr.shape)
919 in_plane_shape = img_shape[axes]
920 if reshape:
921 # Compute transformed input bounds
922 iy, ix = in_plane_shape
923 out_bounds = rot_matrix @ [[0, 0, iy, iy],
924 [0, ix, 0, ix]]
925 # Compute the shape of the transformed input plane
926 out_plane_shape = (out_bounds.ptp(axis=1) + 0.5).astype(int)
927 else:
928 out_plane_shape = img_shape[axes]
930 out_center = rot_matrix @ ((out_plane_shape - 1) / 2)
931 in_center = (in_plane_shape - 1) / 2
932 offset = in_center - out_center
934 output_shape = img_shape
935 output_shape[axes] = out_plane_shape
936 output_shape = tuple(output_shape)
938 complex_output = numpy.iscomplexobj(input_arr)
939 output = _ni_support._get_output(output, input_arr, shape=output_shape,
940 complex_output=complex_output)
942 if ndim <= 2:
943 affine_transform(input_arr, rot_matrix, offset, output_shape, output,
944 order, mode, cval, prefilter)
945 else:
946 # If ndim > 2, the rotation is applied over all the planes
947 # parallel to axes
948 planes_coord = itertools.product(
949 *[[slice(None)] if ax in axes else range(img_shape[ax])
950 for ax in range(ndim)])
952 out_plane_shape = tuple(out_plane_shape)
954 for coordinates in planes_coord:
955 ia = input_arr[coordinates]
956 oa = output[coordinates]
957 affine_transform(ia, rot_matrix, offset, out_plane_shape,
958 oa, order, mode, cval, prefilter)
960 return output