Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/scipy/ndimage/interpolation.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 . import _ni_support
38from . import _nd_image
39from ._ni_docstrings import docdict
40from scipy._lib import doccer
42# Change the default 'reflect' to 'constant' via modifying a copy of docdict
43docdict_copy = docdict.copy()
44del docdict
45docdict_copy['mode'] = docdict_copy['mode'].replace("Default is 'reflect'",
46 "Default is 'constant'")
48docfiller = doccer.filldoc(docdict_copy)
50__all__ = ['spline_filter1d', 'spline_filter', 'geometric_transform',
51 'map_coordinates', 'affine_transform', 'shift', 'zoom', 'rotate']
54@docfiller
55def spline_filter1d(input, order=3, axis=-1, output=numpy.float64,
56 mode='mirror'):
57 """
58 Calculate a 1-D spline filter along the given axis.
60 The lines of the array along the given axis are filtered by a
61 spline filter. The order of the spline must be >= 2 and <= 5.
63 Parameters
64 ----------
65 %(input)s
66 order : int, optional
67 The order of the spline, default is 3.
68 axis : int, optional
69 The axis along which the spline filter is applied. Default is the last
70 axis.
71 output : ndarray or dtype, optional
72 The array in which to place the output, or the dtype of the returned
73 array. Default is ``numpy.float64``.
74 %(mode)s
76 Returns
77 -------
78 spline_filter1d : ndarray
79 The filtered input.
81 Notes
82 -----
83 All functions in `ndimage.interpolation` do spline interpolation of
84 the input image. If using B-splines of `order > 1`, the input image
85 values have to be converted to B-spline coefficients first, which is
86 done by applying this 1-D filter sequentially along all
87 axes of the input. All functions that require B-spline coefficients
88 will automatically filter their inputs, a behavior controllable with
89 the `prefilter` keyword argument. For functions that accept a `mode`
90 parameter, the result will only be correct if it matches the `mode`
91 used when filtering.
93 See Also
94 --------
95 spline_filter : Multidimensional spline filter.
97 Examples
98 --------
99 We can filter an image using 1-D spline along the given axis:
101 >>> from scipy.ndimage import spline_filter1d
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 if numpy.iscomplexobj(input):
121 raise TypeError('Complex type not supported')
122 output = _ni_support._get_output(output, input)
123 if order in [0, 1]:
124 output[...] = numpy.array(input)
125 else:
126 mode = _ni_support._extend_mode_to_code(mode)
127 axis = normalize_axis_index(axis, input.ndim)
128 _nd_image.spline_filter1d(input, order, axis, output, mode)
129 return output
132def spline_filter(input, order=3, output=numpy.float64, mode='mirror'):
133 """
134 Multidimensional spline filter.
136 For more details, see `spline_filter1d`.
138 See Also
139 --------
140 spline_filter1d : Calculate a 1-D spline filter along the given axis.
142 Notes
143 -----
144 The multidimensional filter is implemented as a sequence of
145 1-D spline filters. The intermediate arrays are stored
146 in the same data type as the output. Therefore, for output types
147 with a limited precision, the results may be imprecise because
148 intermediate results may be stored with insufficient precision.
150 Examples
151 --------
152 We can filter an image using multidimentional splines:
154 >>> from scipy.ndimage import spline_filter
155 >>> import matplotlib.pyplot as plt
156 >>> orig_img = np.eye(20) # create an image
157 >>> orig_img[10, :] = 1.0
158 >>> sp_filter = spline_filter(orig_img, order=3)
159 >>> f, ax = plt.subplots(1, 2, sharex=True)
160 >>> for ind, data in enumerate([[orig_img, "original image"],
161 ... [sp_filter, "spline filter"]]):
162 ... ax[ind].imshow(data[0], cmap='gray_r')
163 ... ax[ind].set_title(data[1])
164 >>> plt.tight_layout()
165 >>> plt.show()
167 """
168 if order < 2 or order > 5:
169 raise RuntimeError('spline order not supported')
170 input = numpy.asarray(input)
171 if numpy.iscomplexobj(input):
172 raise TypeError('Complex type not supported')
173 output = _ni_support._get_output(output, input)
174 if order not in [0, 1] and input.ndim > 0:
175 for axis in range(input.ndim):
176 spline_filter1d(input, order, axis, output=output, mode=mode)
177 input = output
178 else:
179 output[...] = input[...]
180 return output
183@docfiller
184def geometric_transform(input, mapping, output_shape=None,
185 output=None, order=3,
186 mode='constant', cval=0.0, prefilter=True,
187 extra_arguments=(), extra_keywords={}):
188 """
189 Apply an arbitrary geometric transform.
191 The given mapping function is used to find, for each point in the
192 output, the corresponding coordinates in the input. The value of the
193 input at those coordinates is determined by spline interpolation of
194 the requested order.
196 Parameters
197 ----------
198 %(input)s
199 mapping : {callable, scipy.LowLevelCallable}
200 A callable object that accepts a tuple of length equal to the output
201 array rank, and returns the corresponding input coordinates as a tuple
202 of length equal to the input array rank.
203 output_shape : tuple of ints, optional
204 Shape tuple.
205 %(output)s
206 order : int, optional
207 The order of the spline interpolation, default is 3.
208 The order has to be in the range 0-5.
209 %(mode)s
210 %(cval)s
211 %(prefilter)s
212 extra_arguments : tuple, optional
213 Extra arguments passed to `mapping`.
214 extra_keywords : dict, optional
215 Extra keywords passed to `mapping`.
217 Returns
218 -------
219 output : ndarray
220 The filtered input.
222 See Also
223 --------
224 map_coordinates, affine_transform, spline_filter1d
227 Notes
228 -----
229 This function also accepts low-level callback functions with one
230 the following signatures and wrapped in `scipy.LowLevelCallable`:
232 .. code:: c
234 int mapping(npy_intp *output_coordinates, double *input_coordinates,
235 int output_rank, int input_rank, void *user_data)
236 int mapping(intptr_t *output_coordinates, double *input_coordinates,
237 int output_rank, int input_rank, void *user_data)
239 The calling function iterates over the elements of the output array,
240 calling the callback function at each element. The coordinates of the
241 current output element are passed through ``output_coordinates``. The
242 callback function must return the coordinates at which the input must
243 be interpolated in ``input_coordinates``. The rank of the input and
244 output arrays are given by ``input_rank`` and ``output_rank``
245 respectively. ``user_data`` is the data pointer provided
246 to `scipy.LowLevelCallable` as-is.
248 The callback function must return an integer error status that is zero
249 if something went wrong and one otherwise. If an error occurs, you should
250 normally set the Python error status with an informative message
251 before returning, otherwise a default error message is set by the
252 calling function.
254 In addition, some other low-level function pointer specifications
255 are accepted, but these are for backward compatibility only and should
256 not be used in new code.
258 Examples
259 --------
260 >>> import numpy as np
261 >>> from scipy.ndimage import geometric_transform
262 >>> a = np.arange(12.).reshape((4, 3))
263 >>> def shift_func(output_coords):
264 ... return (output_coords[0] - 0.5, output_coords[1] - 0.5)
265 ...
266 >>> geometric_transform(a, shift_func)
267 array([[ 0. , 0. , 0. ],
268 [ 0. , 1.362, 2.738],
269 [ 0. , 4.812, 6.187],
270 [ 0. , 8.263, 9.637]])
272 >>> b = [1, 2, 3, 4, 5]
273 >>> def shift_func(output_coords):
274 ... return (output_coords[0] - 3,)
275 ...
276 >>> geometric_transform(b, shift_func, mode='constant')
277 array([0, 0, 0, 1, 2])
278 >>> geometric_transform(b, shift_func, mode='nearest')
279 array([1, 1, 1, 1, 2])
280 >>> geometric_transform(b, shift_func, mode='reflect')
281 array([3, 2, 1, 1, 2])
282 >>> geometric_transform(b, shift_func, mode='wrap')
283 array([2, 3, 4, 1, 2])
285 """
286 if order < 0 or order > 5:
287 raise RuntimeError('spline order not supported')
288 input = numpy.asarray(input)
289 if numpy.iscomplexobj(input):
290 raise TypeError('Complex type not supported')
291 if output_shape is None:
292 output_shape = input.shape
293 if input.ndim < 1 or len(output_shape) < 1:
294 raise RuntimeError('input and output rank must be > 0')
295 mode = _ni_support._extend_mode_to_code(mode)
296 if prefilter and order > 1:
297 filtered = spline_filter(input, order, output=numpy.float64)
298 else:
299 filtered = input
300 output = _ni_support._get_output(output, input, shape=output_shape)
301 _nd_image.geometric_transform(filtered, mapping, None, None, None, output,
302 order, mode, cval, extra_arguments,
303 extra_keywords)
304 return output
307@docfiller
308def map_coordinates(input, coordinates, output=None, order=3,
309 mode='constant', cval=0.0, prefilter=True):
310 """
311 Map the input array to new coordinates by interpolation.
313 The array of coordinates is used to find, for each point in the output,
314 the corresponding coordinates in the input. The value of the input at
315 those coordinates is determined by spline interpolation of the
316 requested order.
318 The shape of the output is derived from that of the coordinate
319 array by dropping the first axis. The values of the array along
320 the first axis are the coordinates in the input array at which the
321 output value is found.
323 Parameters
324 ----------
325 %(input)s
326 coordinates : array_like
327 The coordinates at which `input` is evaluated.
328 %(output)s
329 order : int, optional
330 The order of the spline interpolation, default is 3.
331 The order has to be in the range 0-5.
332 %(mode)s
333 %(cval)s
334 %(prefilter)s
336 Returns
337 -------
338 map_coordinates : ndarray
339 The result of transforming the input. The shape of the output is
340 derived from that of `coordinates` by dropping the first axis.
342 See Also
343 --------
344 spline_filter, geometric_transform, scipy.interpolate
346 Examples
347 --------
348 >>> from scipy import ndimage
349 >>> a = np.arange(12.).reshape((4, 3))
350 >>> a
351 array([[ 0., 1., 2.],
352 [ 3., 4., 5.],
353 [ 6., 7., 8.],
354 [ 9., 10., 11.]])
355 >>> ndimage.map_coordinates(a, [[0.5, 2], [0.5, 1]], order=1)
356 array([ 2., 7.])
358 Above, the interpolated value of a[0.5, 0.5] gives output[0], while
359 a[2, 1] is output[1].
361 >>> inds = np.array([[0.5, 2], [0.5, 4]])
362 >>> ndimage.map_coordinates(a, inds, order=1, cval=-33.3)
363 array([ 2. , -33.3])
364 >>> ndimage.map_coordinates(a, inds, order=1, mode='nearest')
365 array([ 2., 8.])
366 >>> ndimage.map_coordinates(a, inds, order=1, cval=0, output=bool)
367 array([ True, False], dtype=bool)
369 """
370 if order < 0 or order > 5:
371 raise RuntimeError('spline order not supported')
372 input = numpy.asarray(input)
373 if numpy.iscomplexobj(input):
374 raise TypeError('Complex type not supported')
375 coordinates = numpy.asarray(coordinates)
376 if numpy.iscomplexobj(coordinates):
377 raise TypeError('Complex type not supported')
378 output_shape = coordinates.shape[1:]
379 if input.ndim < 1 or len(output_shape) < 1:
380 raise RuntimeError('input and output rank must be > 0')
381 if coordinates.shape[0] != input.ndim:
382 raise RuntimeError('invalid shape for coordinate array')
383 mode = _ni_support._extend_mode_to_code(mode)
384 if prefilter and order > 1:
385 filtered = spline_filter(input, order, output=numpy.float64)
386 else:
387 filtered = input
388 output = _ni_support._get_output(output, input,
389 shape=output_shape)
390 _nd_image.geometric_transform(filtered, None, coordinates, None, None,
391 output, order, mode, cval, None, None)
392 return output
395@docfiller
396def affine_transform(input, matrix, offset=0.0, output_shape=None,
397 output=None, order=3,
398 mode='constant', cval=0.0, prefilter=True):
399 """
400 Apply an affine transformation.
402 Given an output image pixel index vector ``o``, the pixel value
403 is determined from the input image at position
404 ``np.dot(matrix, o) + offset``.
406 This does 'pull' (or 'backward') resampling, transforming the output space
407 to the input to locate data. Affine transformations are often described in
408 the 'push' (or 'forward') direction, transforming input to output. If you
409 have a matrix for the 'push' transformation, use its inverse
410 (:func:`numpy.linalg.inv`) in this function.
412 Parameters
413 ----------
414 %(input)s
415 matrix : ndarray
416 The inverse coordinate transformation matrix, mapping output
417 coordinates to input coordinates. If ``ndim`` is the number of
418 dimensions of ``input``, the given matrix must have one of the
419 following shapes:
421 - ``(ndim, ndim)``: the linear transformation matrix for each
422 output coordinate.
423 - ``(ndim,)``: assume that the 2-D transformation matrix is
424 diagonal, with the diagonal specified by the given value. A more
425 efficient algorithm is then used that exploits the separability
426 of the problem.
427 - ``(ndim + 1, ndim + 1)``: assume that the transformation is
428 specified using homogeneous coordinates [1]_. In this case, any
429 value passed to ``offset`` is ignored.
430 - ``(ndim, ndim + 1)``: as above, but the bottom row of a
431 homogeneous transformation matrix is always ``[0, 0, ..., 1]``,
432 and may be omitted.
434 offset : float or sequence, optional
435 The offset into the array where the transform is applied. If a float,
436 `offset` is the same for each axis. If a sequence, `offset` should
437 contain one value for each axis.
438 output_shape : tuple of ints, optional
439 Shape tuple.
440 %(output)s
441 order : int, optional
442 The order of the spline interpolation, default is 3.
443 The order has to be in the range 0-5.
444 %(mode)s
445 %(cval)s
446 %(prefilter)s
448 Returns
449 -------
450 affine_transform : ndarray
451 The transformed input.
453 Notes
454 -----
455 The given matrix and offset are used to find for each point in the
456 output the corresponding coordinates in the input by an affine
457 transformation. The value of the input at those coordinates is
458 determined by spline interpolation of the requested order. Points
459 outside the boundaries of the input are filled according to the given
460 mode.
462 .. versionchanged:: 0.18.0
463 Previously, the exact interpretation of the affine transformation
464 depended on whether the matrix was supplied as a 1-D or a
465 2-D array. If a 1-D array was supplied
466 to the matrix parameter, the output pixel value at index ``o``
467 was determined from the input image at position
468 ``matrix * (o + offset)``.
470 References
471 ----------
472 .. [1] https://en.wikipedia.org/wiki/Homogeneous_coordinates
473 """
474 if order < 0 or order > 5:
475 raise RuntimeError('spline order not supported')
476 input = numpy.asarray(input)
477 if numpy.iscomplexobj(input):
478 raise TypeError('Complex type not supported')
479 if output_shape is None:
480 output_shape = input.shape
481 if input.ndim < 1 or len(output_shape) < 1:
482 raise RuntimeError('input and output rank must be > 0')
483 mode = _ni_support._extend_mode_to_code(mode)
484 if prefilter and order > 1:
485 filtered = spline_filter(input, order, output=numpy.float64)
486 else:
487 filtered = input
488 output = _ni_support._get_output(output, input,
489 shape=output_shape)
490 matrix = numpy.asarray(matrix, dtype=numpy.float64)
491 if matrix.ndim not in [1, 2] or matrix.shape[0] < 1:
492 raise RuntimeError('no proper affine matrix provided')
493 if (matrix.ndim == 2 and matrix.shape[1] == input.ndim + 1 and
494 (matrix.shape[0] in [input.ndim, input.ndim + 1])):
495 if matrix.shape[0] == input.ndim + 1:
496 exptd = [0] * input.ndim + [1]
497 if not numpy.all(matrix[input.ndim] == exptd):
498 msg = ('Expected homogeneous transformation matrix with '
499 'shape %s for image shape %s, but bottom row was '
500 'not equal to %s' % (matrix.shape, input.shape, exptd))
501 raise ValueError(msg)
502 # assume input is homogeneous coordinate transformation matrix
503 offset = matrix[:input.ndim, input.ndim]
504 matrix = matrix[:input.ndim, :input.ndim]
505 if matrix.shape[0] != input.ndim:
506 raise RuntimeError('affine matrix has wrong number of rows')
507 if matrix.ndim == 2 and matrix.shape[1] != output.ndim:
508 raise RuntimeError('affine matrix has wrong number of columns')
509 if not matrix.flags.contiguous:
510 matrix = matrix.copy()
511 offset = _ni_support._normalize_sequence(offset, input.ndim)
512 offset = numpy.asarray(offset, dtype=numpy.float64)
513 if offset.ndim != 1 or offset.shape[0] < 1:
514 raise RuntimeError('no proper offset provided')
515 if not offset.flags.contiguous:
516 offset = offset.copy()
517 if matrix.ndim == 1:
518 warnings.warn(
519 "The behavior of affine_transform with a 1-D "
520 "array supplied for the matrix parameter has changed in "
521 "SciPy 0.18.0."
522 )
523 _nd_image.zoom_shift(filtered, matrix, offset/matrix, output, order,
524 mode, cval)
525 else:
526 _nd_image.geometric_transform(filtered, None, None, matrix, offset,
527 output, order, mode, cval, None, None)
528 return output
531@docfiller
532def shift(input, shift, output=None, order=3, mode='constant', cval=0.0,
533 prefilter=True):
534 """
535 Shift an array.
537 The array is shifted using spline interpolation of the requested order.
538 Points outside the boundaries of the input are filled according to the
539 given mode.
541 Parameters
542 ----------
543 %(input)s
544 shift : float or sequence
545 The shift along the axes. If a float, `shift` is the same for each
546 axis. If a sequence, `shift` should contain one value for each axis.
547 %(output)s
548 order : int, optional
549 The order of the spline interpolation, default is 3.
550 The order has to be in the range 0-5.
551 %(mode)s
552 %(cval)s
553 %(prefilter)s
555 Returns
556 -------
557 shift : ndarray
558 The shifted input.
560 """
561 if order < 0 or order > 5:
562 raise RuntimeError('spline order not supported')
563 input = numpy.asarray(input)
564 if numpy.iscomplexobj(input):
565 raise TypeError('Complex type not supported')
566 if input.ndim < 1:
567 raise RuntimeError('input and output rank must be > 0')
568 mode = _ni_support._extend_mode_to_code(mode)
569 if prefilter and order > 1:
570 filtered = spline_filter(input, order, output=numpy.float64)
571 else:
572 filtered = input
573 output = _ni_support._get_output(output, input)
574 shift = _ni_support._normalize_sequence(shift, input.ndim)
575 shift = [-ii for ii in shift]
576 shift = numpy.asarray(shift, dtype=numpy.float64)
577 if not shift.flags.contiguous:
578 shift = shift.copy()
579 _nd_image.zoom_shift(filtered, None, shift, output, order, mode, cval)
580 return output
583@docfiller
584def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0,
585 prefilter=True):
586 """
587 Zoom an array.
589 The array is zoomed using spline interpolation of the requested order.
591 Parameters
592 ----------
593 %(input)s
594 zoom : float or sequence
595 The zoom factor along the axes. If a float, `zoom` is the same for each
596 axis. If a sequence, `zoom` should contain one value for each axis.
597 %(output)s
598 order : int, optional
599 The order of the spline interpolation, default is 3.
600 The order has to be in the range 0-5.
601 %(mode)s
602 %(cval)s
603 %(prefilter)s
605 Returns
606 -------
607 zoom : ndarray
608 The zoomed input.
610 Examples
611 --------
612 >>> from scipy import ndimage, misc
613 >>> import matplotlib.pyplot as plt
615 >>> fig = plt.figure()
616 >>> ax1 = fig.add_subplot(121) # left side
617 >>> ax2 = fig.add_subplot(122) # right side
618 >>> ascent = misc.ascent()
619 >>> result = ndimage.zoom(ascent, 3.0)
620 >>> ax1.imshow(ascent)
621 >>> ax2.imshow(result)
622 >>> plt.show()
624 >>> print(ascent.shape)
625 (512, 512)
627 >>> print(result.shape)
628 (1536, 1536)
629 """
630 if order < 0 or order > 5:
631 raise RuntimeError('spline order not supported')
632 input = numpy.asarray(input)
633 if numpy.iscomplexobj(input):
634 raise TypeError('Complex type not supported')
635 if input.ndim < 1:
636 raise RuntimeError('input and output rank must be > 0')
637 mode = _ni_support._extend_mode_to_code(mode)
638 if prefilter and order > 1:
639 filtered = spline_filter(input, order, output=numpy.float64)
640 else:
641 filtered = input
642 zoom = _ni_support._normalize_sequence(zoom, input.ndim)
643 output_shape = tuple(
644 [int(round(ii * jj)) for ii, jj in zip(input.shape, zoom)])
646 zoom_div = numpy.array(output_shape, float) - 1
647 # Zooming to infinite values is unpredictable, so just choose
648 # zoom factor 1 instead
649 zoom = numpy.divide(numpy.array(input.shape) - 1, zoom_div,
650 out=numpy.ones_like(input.shape, dtype=numpy.float64),
651 where=zoom_div != 0)
653 output = _ni_support._get_output(output, input,
654 shape=output_shape)
655 zoom = numpy.ascontiguousarray(zoom)
656 _nd_image.zoom_shift(filtered, zoom, None, output, order, mode, cval)
657 return output
660@docfiller
661def rotate(input, angle, axes=(1, 0), reshape=True, output=None, order=3,
662 mode='constant', cval=0.0, prefilter=True):
663 """
664 Rotate an array.
666 The array is rotated in the plane defined by the two axes given by the
667 `axes` parameter using spline interpolation of the requested order.
669 Parameters
670 ----------
671 %(input)s
672 angle : float
673 The rotation angle in degrees.
674 axes : tuple of 2 ints, optional
675 The two axes that define the plane of rotation. Default is the first
676 two axes.
677 reshape : bool, optional
678 If `reshape` is true, the output shape is adapted so that the input
679 array is contained completely in the output. Default is True.
680 %(output)s
681 order : int, optional
682 The order of the spline interpolation, default is 3.
683 The order has to be in the range 0-5.
684 %(mode)s
685 %(cval)s
686 %(prefilter)s
688 Returns
689 -------
690 rotate : ndarray
691 The rotated input.
693 Examples
694 --------
695 >>> from scipy import ndimage, misc
696 >>> import matplotlib.pyplot as plt
697 >>> fig = plt.figure(figsize=(10, 3))
698 >>> ax1, ax2, ax3 = fig.subplots(1, 3)
699 >>> img = misc.ascent()
700 >>> img_45 = ndimage.rotate(img, 45, reshape=False)
701 >>> full_img_45 = ndimage.rotate(img, 45, reshape=True)
702 >>> ax1.imshow(img, cmap='gray')
703 >>> ax1.set_axis_off()
704 >>> ax2.imshow(img_45, cmap='gray')
705 >>> ax2.set_axis_off()
706 >>> ax3.imshow(full_img_45, cmap='gray')
707 >>> ax3.set_axis_off()
708 >>> fig.set_tight_layout(True)
709 >>> plt.show()
710 >>> print(img.shape)
711 (512, 512)
712 >>> print(img_45.shape)
713 (512, 512)
714 >>> print(full_img_45.shape)
715 (724, 724)
717 """
718 input_arr = numpy.asarray(input)
719 ndim = input_arr.ndim
721 if ndim < 2:
722 raise ValueError('input array should be at least 2D')
724 axes = list(axes)
726 if len(axes) != 2:
727 raise ValueError('axes should contain exactly two values')
729 if not all([float(ax).is_integer() for ax in axes]):
730 raise ValueError('axes should contain only integer values')
732 if axes[0] < 0:
733 axes[0] += ndim
734 if axes[1] < 0:
735 axes[1] += ndim
736 if axes[0] < 0 or axes[1] < 0 or axes[0] >= ndim or axes[1] >= ndim:
737 raise ValueError('invalid rotation plane specified')
739 axes.sort()
741 angle_rad = numpy.deg2rad(angle)
742 c, s = numpy.cos(angle_rad), numpy.sin(angle_rad)
744 rot_matrix = numpy.array([[c, s],
745 [-s, c]])
747 img_shape = numpy.asarray(input_arr.shape)
748 in_plane_shape = img_shape[axes]
749 if reshape:
750 # Compute transformed input bounds
751 iy, ix = in_plane_shape
752 out_bounds = rot_matrix @ [[0, 0, iy, iy],
753 [0, ix, 0, ix]]
754 # Compute the shape of the transformed input plane
755 out_plane_shape = (out_bounds.ptp(axis=1) + 0.5).astype(int)
756 else:
757 out_plane_shape = img_shape[axes]
759 out_center = rot_matrix @ ((out_plane_shape - 1) / 2)
760 in_center = (in_plane_shape - 1) / 2
761 offset = in_center - out_center
763 output_shape = img_shape
764 output_shape[axes] = out_plane_shape
765 output_shape = tuple(output_shape)
767 output = _ni_support._get_output(output, input_arr, shape=output_shape)
769 if ndim <= 2:
770 affine_transform(input_arr, rot_matrix, offset, output_shape, output,
771 order, mode, cval, prefilter)
772 else:
773 # If ndim > 2, the rotation is applied over all the planes
774 # parallel to axes
775 planes_coord = itertools.product(
776 *[[slice(None)] if ax in axes else range(img_shape[ax])
777 for ax in range(ndim)])
779 out_plane_shape = tuple(out_plane_shape)
781 for coordinates in planes_coord:
782 ia = input_arr[coordinates]
783 oa = output[coordinates]
784 affine_transform(ia, rot_matrix, offset, out_plane_shape,
785 oa, order, mode, cval, prefilter)
787 return output