interpolation.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  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.
  30. from __future__ import division, print_function, absolute_import
  31. import math
  32. import numpy
  33. import warnings
  34. from . import _ni_support
  35. from . import _nd_image
  36. from ._ni_docstrings import docdict
  37. from scipy.misc import doccer
  38. # Change the default 'reflect' to 'constant' via modifying a copy of docdict
  39. docdict_copy = docdict.copy()
  40. del docdict
  41. docdict_copy['mode'] = docdict_copy['mode'].replace("Default is 'reflect'",
  42. "Default is 'constant'")
  43. docfiller = doccer.filldoc(docdict_copy)
  44. __all__ = ['spline_filter1d', 'spline_filter', 'geometric_transform',
  45. 'map_coordinates', 'affine_transform', 'shift', 'zoom', 'rotate']
  46. @docfiller
  47. def spline_filter1d(input, order=3, axis=-1, output=numpy.float64,
  48. mode='mirror'):
  49. """
  50. Calculate a one-dimensional spline filter along the given axis.
  51. The lines of the array along the given axis are filtered by a
  52. spline filter. The order of the spline must be >= 2 and <= 5.
  53. Parameters
  54. ----------
  55. %(input)s
  56. order : int, optional
  57. The order of the spline, default is 3.
  58. axis : int, optional
  59. The axis along which the spline filter is applied. Default is the last
  60. axis.
  61. output : ndarray or dtype, optional
  62. The array in which to place the output, or the dtype of the returned
  63. array. Default is `numpy.float64`.
  64. %(mode)s
  65. Returns
  66. -------
  67. spline_filter1d : ndarray
  68. The filtered input.
  69. Notes
  70. -----
  71. All functions in `ndimage.interpolation` do spline interpolation of
  72. the input image. If using b-splines of `order > 1`, the input image
  73. values have to be converted to b-spline coefficients first, which is
  74. done by applying this one-dimensional filter sequentially along all
  75. axes of the input. All functions that require b-spline coefficients
  76. will automatically filter their inputs, a behavior controllable with
  77. the `prefilter` keyword argument. For functions that accept a `mode`
  78. parameter, the result will only be correct if it matches the `mode`
  79. used when filtering.
  80. """
  81. if order < 0 or order > 5:
  82. raise RuntimeError('spline order not supported')
  83. input = numpy.asarray(input)
  84. if numpy.iscomplexobj(input):
  85. raise TypeError('Complex type not supported')
  86. output = _ni_support._get_output(output, input)
  87. if order in [0, 1]:
  88. output[...] = numpy.array(input)
  89. else:
  90. mode = _ni_support._extend_mode_to_code(mode)
  91. axis = _ni_support._check_axis(axis, input.ndim)
  92. _nd_image.spline_filter1d(input, order, axis, output, mode)
  93. return output
  94. def spline_filter(input, order=3, output=numpy.float64, mode='mirror'):
  95. """
  96. Multi-dimensional spline filter.
  97. For more details, see `spline_filter1d`.
  98. See Also
  99. --------
  100. spline_filter1d
  101. Notes
  102. -----
  103. The multi-dimensional filter is implemented as a sequence of
  104. one-dimensional spline filters. The intermediate arrays are stored
  105. in the same data type as the output. Therefore, for output types
  106. with a limited precision, the results may be imprecise because
  107. intermediate results may be stored with insufficient precision.
  108. """
  109. if order < 2 or order > 5:
  110. raise RuntimeError('spline order not supported')
  111. input = numpy.asarray(input)
  112. if numpy.iscomplexobj(input):
  113. raise TypeError('Complex type not supported')
  114. output = _ni_support._get_output(output, input)
  115. if order not in [0, 1] and input.ndim > 0:
  116. for axis in range(input.ndim):
  117. spline_filter1d(input, order, axis, output=output, mode=mode)
  118. input = output
  119. else:
  120. output[...] = input[...]
  121. return output
  122. @docfiller
  123. def geometric_transform(input, mapping, output_shape=None,
  124. output=None, order=3,
  125. mode='constant', cval=0.0, prefilter=True,
  126. extra_arguments=(), extra_keywords={}):
  127. """
  128. Apply an arbitrary geometric transform.
  129. The given mapping function is used to find, for each point in the
  130. output, the corresponding coordinates in the input. The value of the
  131. input at those coordinates is determined by spline interpolation of
  132. the requested order.
  133. Parameters
  134. ----------
  135. %(input)s
  136. mapping : {callable, scipy.LowLevelCallable}
  137. A callable object that accepts a tuple of length equal to the output
  138. array rank, and returns the corresponding input coordinates as a tuple
  139. of length equal to the input array rank.
  140. output_shape : tuple of ints, optional
  141. Shape tuple.
  142. %(output)s
  143. order : int, optional
  144. The order of the spline interpolation, default is 3.
  145. The order has to be in the range 0-5.
  146. %(mode)s
  147. %(cval)s
  148. %(prefilter)s
  149. extra_arguments : tuple, optional
  150. Extra arguments passed to `mapping`.
  151. extra_keywords : dict, optional
  152. Extra keywords passed to `mapping`.
  153. Returns
  154. -------
  155. output : ndarray
  156. The filtered input.
  157. See Also
  158. --------
  159. map_coordinates, affine_transform, spline_filter1d
  160. Notes
  161. -----
  162. This function also accepts low-level callback functions with one
  163. the following signatures and wrapped in `scipy.LowLevelCallable`:
  164. .. code:: c
  165. int mapping(npy_intp *output_coordinates, double *input_coordinates,
  166. int output_rank, int input_rank, void *user_data)
  167. int mapping(intptr_t *output_coordinates, double *input_coordinates,
  168. int output_rank, int input_rank, void *user_data)
  169. The calling function iterates over the elements of the output array,
  170. calling the callback function at each element. The coordinates of the
  171. current output element are passed through ``output_coordinates``. The
  172. callback function must return the coordinates at which the input must
  173. be interpolated in ``input_coordinates``. The rank of the input and
  174. output arrays are given by ``input_rank`` and ``output_rank``
  175. respectively. ``user_data`` is the data pointer provided
  176. to `scipy.LowLevelCallable` as-is.
  177. The callback function must return an integer error status that is zero
  178. if something went wrong and one otherwise. If an error occurs, you should
  179. normally set the python error status with an informative message
  180. before returning, otherwise a default error message is set by the
  181. calling function.
  182. In addition, some other low-level function pointer specifications
  183. are accepted, but these are for backward compatibility only and should
  184. not be used in new code.
  185. Examples
  186. --------
  187. >>> import numpy as np
  188. >>> from scipy.ndimage import geometric_transform
  189. >>> a = np.arange(12.).reshape((4, 3))
  190. >>> def shift_func(output_coords):
  191. ... return (output_coords[0] - 0.5, output_coords[1] - 0.5)
  192. ...
  193. >>> geometric_transform(a, shift_func)
  194. array([[ 0. , 0. , 0. ],
  195. [ 0. , 1.362, 2.738],
  196. [ 0. , 4.812, 6.187],
  197. [ 0. , 8.263, 9.637]])
  198. >>> b = [1, 2, 3, 4, 5]
  199. >>> def shift_func(output_coords):
  200. ... return (output_coords[0] - 3,)
  201. ...
  202. >>> geometric_transform(b, shift_func, mode='constant')
  203. array([0, 0, 0, 1, 2])
  204. >>> geometric_transform(b, shift_func, mode='nearest')
  205. array([1, 1, 1, 1, 2])
  206. >>> geometric_transform(b, shift_func, mode='reflect')
  207. array([3, 2, 1, 1, 2])
  208. >>> geometric_transform(b, shift_func, mode='wrap')
  209. array([2, 3, 4, 1, 2])
  210. """
  211. if order < 0 or order > 5:
  212. raise RuntimeError('spline order not supported')
  213. input = numpy.asarray(input)
  214. if numpy.iscomplexobj(input):
  215. raise TypeError('Complex type not supported')
  216. if output_shape is None:
  217. output_shape = input.shape
  218. if input.ndim < 1 or len(output_shape) < 1:
  219. raise RuntimeError('input and output rank must be > 0')
  220. mode = _ni_support._extend_mode_to_code(mode)
  221. if prefilter and order > 1:
  222. filtered = spline_filter(input, order, output=numpy.float64)
  223. else:
  224. filtered = input
  225. output = _ni_support._get_output(output, input, shape=output_shape)
  226. _nd_image.geometric_transform(filtered, mapping, None, None, None, output,
  227. order, mode, cval, extra_arguments,
  228. extra_keywords)
  229. return output
  230. @docfiller
  231. def map_coordinates(input, coordinates, output=None, order=3,
  232. mode='constant', cval=0.0, prefilter=True):
  233. """
  234. Map the input array to new coordinates by interpolation.
  235. The array of coordinates is used to find, for each point in the output,
  236. the corresponding coordinates in the input. The value of the input at
  237. those coordinates is determined by spline interpolation of the
  238. requested order.
  239. The shape of the output is derived from that of the coordinate
  240. array by dropping the first axis. The values of the array along
  241. the first axis are the coordinates in the input array at which the
  242. output value is found.
  243. Parameters
  244. ----------
  245. %(input)s
  246. coordinates : array_like
  247. The coordinates at which `input` is evaluated.
  248. %(output)s
  249. order : int, optional
  250. The order of the spline interpolation, default is 3.
  251. The order has to be in the range 0-5.
  252. %(mode)s
  253. %(cval)s
  254. %(prefilter)s
  255. Returns
  256. -------
  257. map_coordinates : ndarray
  258. The result of transforming the input. The shape of the output is
  259. derived from that of `coordinates` by dropping the first axis.
  260. See Also
  261. --------
  262. spline_filter, geometric_transform, scipy.interpolate
  263. Examples
  264. --------
  265. >>> from scipy import ndimage
  266. >>> a = np.arange(12.).reshape((4, 3))
  267. >>> a
  268. array([[ 0., 1., 2.],
  269. [ 3., 4., 5.],
  270. [ 6., 7., 8.],
  271. [ 9., 10., 11.]])
  272. >>> ndimage.map_coordinates(a, [[0.5, 2], [0.5, 1]], order=1)
  273. array([ 2., 7.])
  274. Above, the interpolated value of a[0.5, 0.5] gives output[0], while
  275. a[2, 1] is output[1].
  276. >>> inds = np.array([[0.5, 2], [0.5, 4]])
  277. >>> ndimage.map_coordinates(a, inds, order=1, cval=-33.3)
  278. array([ 2. , -33.3])
  279. >>> ndimage.map_coordinates(a, inds, order=1, mode='nearest')
  280. array([ 2., 8.])
  281. >>> ndimage.map_coordinates(a, inds, order=1, cval=0, output=bool)
  282. array([ True, False], dtype=bool)
  283. """
  284. if order < 0 or order > 5:
  285. raise RuntimeError('spline order not supported')
  286. input = numpy.asarray(input)
  287. if numpy.iscomplexobj(input):
  288. raise TypeError('Complex type not supported')
  289. coordinates = numpy.asarray(coordinates)
  290. if numpy.iscomplexobj(coordinates):
  291. raise TypeError('Complex type not supported')
  292. output_shape = coordinates.shape[1:]
  293. if input.ndim < 1 or len(output_shape) < 1:
  294. raise RuntimeError('input and output rank must be > 0')
  295. if coordinates.shape[0] != input.ndim:
  296. raise RuntimeError('invalid shape for coordinate array')
  297. mode = _ni_support._extend_mode_to_code(mode)
  298. if prefilter and order > 1:
  299. filtered = spline_filter(input, order, output=numpy.float64)
  300. else:
  301. filtered = input
  302. output = _ni_support._get_output(output, input,
  303. shape=output_shape)
  304. _nd_image.geometric_transform(filtered, None, coordinates, None, None,
  305. output, order, mode, cval, None, None)
  306. return output
  307. @docfiller
  308. def affine_transform(input, matrix, offset=0.0, output_shape=None,
  309. output=None, order=3,
  310. mode='constant', cval=0.0, prefilter=True):
  311. """
  312. Apply an affine transformation.
  313. Given an output image pixel index vector ``o``, the pixel value
  314. is determined from the input image at position
  315. ``np.dot(matrix, o) + offset``.
  316. Parameters
  317. ----------
  318. %(input)s
  319. matrix : ndarray
  320. The inverse coordinate transformation matrix, mapping output
  321. coordinates to input coordinates. If ``ndim`` is the number of
  322. dimensions of ``input``, the given matrix must have one of the
  323. following shapes:
  324. - ``(ndim, ndim)``: the linear transformation matrix for each
  325. output coordinate.
  326. - ``(ndim,)``: assume that the 2D transformation matrix is
  327. diagonal, with the diagonal specified by the given value. A more
  328. efficient algorithm is then used that exploits the separability
  329. of the problem.
  330. - ``(ndim + 1, ndim + 1)``: assume that the transformation is
  331. specified using homogeneous coordinates [1]_. In this case, any
  332. value passed to ``offset`` is ignored.
  333. - ``(ndim, ndim + 1)``: as above, but the bottom row of a
  334. homogeneous transformation matrix is always ``[0, 0, ..., 1]``,
  335. and may be omitted.
  336. offset : float or sequence, optional
  337. The offset into the array where the transform is applied. If a float,
  338. `offset` is the same for each axis. If a sequence, `offset` should
  339. contain one value for each axis.
  340. output_shape : tuple of ints, optional
  341. Shape tuple.
  342. %(output)s
  343. order : int, optional
  344. The order of the spline interpolation, default is 3.
  345. The order has to be in the range 0-5.
  346. %(mode)s
  347. %(cval)s
  348. %(prefilter)s
  349. Returns
  350. -------
  351. affine_transform : ndarray
  352. The transformed input.
  353. Notes
  354. -----
  355. The given matrix and offset are used to find for each point in the
  356. output the corresponding coordinates in the input by an affine
  357. transformation. The value of the input at those coordinates is
  358. determined by spline interpolation of the requested order. Points
  359. outside the boundaries of the input are filled according to the given
  360. mode.
  361. .. versionchanged:: 0.18.0
  362. Previously, the exact interpretation of the affine transformation
  363. depended on whether the matrix was supplied as a one-dimensional or
  364. two-dimensional array. If a one-dimensional array was supplied
  365. to the matrix parameter, the output pixel value at index ``o``
  366. was determined from the input image at position
  367. ``matrix * (o + offset)``.
  368. References
  369. ----------
  370. .. [1] https://en.wikipedia.org/wiki/Homogeneous_coordinates
  371. """
  372. if order < 0 or order > 5:
  373. raise RuntimeError('spline order not supported')
  374. input = numpy.asarray(input)
  375. if numpy.iscomplexobj(input):
  376. raise TypeError('Complex type not supported')
  377. if output_shape is None:
  378. output_shape = input.shape
  379. if input.ndim < 1 or len(output_shape) < 1:
  380. raise RuntimeError('input and output rank must be > 0')
  381. mode = _ni_support._extend_mode_to_code(mode)
  382. if prefilter and order > 1:
  383. filtered = spline_filter(input, order, output=numpy.float64)
  384. else:
  385. filtered = input
  386. output = _ni_support._get_output(output, input,
  387. shape=output_shape)
  388. matrix = numpy.asarray(matrix, dtype=numpy.float64)
  389. if matrix.ndim not in [1, 2] or matrix.shape[0] < 1:
  390. raise RuntimeError('no proper affine matrix provided')
  391. if (matrix.ndim == 2 and matrix.shape[1] == input.ndim + 1 and
  392. (matrix.shape[0] in [input.ndim, input.ndim + 1])):
  393. if matrix.shape[0] == input.ndim + 1:
  394. exptd = [0] * input.ndim + [1]
  395. if not numpy.all(matrix[input.ndim] == exptd):
  396. msg = ('Expected homogeneous transformation matrix with '
  397. 'shape %s for image shape %s, but bottom row was '
  398. 'not equal to %s' % (matrix.shape, input.shape, exptd))
  399. raise ValueError(msg)
  400. # assume input is homogeneous coordinate transformation matrix
  401. offset = matrix[:input.ndim, input.ndim]
  402. matrix = matrix[:input.ndim, :input.ndim]
  403. if matrix.shape[0] != input.ndim:
  404. raise RuntimeError('affine matrix has wrong number of rows')
  405. if matrix.ndim == 2 and matrix.shape[1] != output.ndim:
  406. raise RuntimeError('affine matrix has wrong number of columns')
  407. if not matrix.flags.contiguous:
  408. matrix = matrix.copy()
  409. offset = _ni_support._normalize_sequence(offset, input.ndim)
  410. offset = numpy.asarray(offset, dtype=numpy.float64)
  411. if offset.ndim != 1 or offset.shape[0] < 1:
  412. raise RuntimeError('no proper offset provided')
  413. if not offset.flags.contiguous:
  414. offset = offset.copy()
  415. if matrix.ndim == 1:
  416. warnings.warn(
  417. "The behaviour of affine_transform with a one-dimensional "
  418. "array supplied for the matrix parameter has changed in "
  419. "scipy 0.18.0."
  420. )
  421. _nd_image.zoom_shift(filtered, matrix, offset/matrix, output, order,
  422. mode, cval)
  423. else:
  424. _nd_image.geometric_transform(filtered, None, None, matrix, offset,
  425. output, order, mode, cval, None, None)
  426. return output
  427. @docfiller
  428. def shift(input, shift, output=None, order=3, mode='constant', cval=0.0,
  429. prefilter=True):
  430. """
  431. Shift an array.
  432. The array is shifted using spline interpolation of the requested order.
  433. Points outside the boundaries of the input are filled according to the
  434. given mode.
  435. Parameters
  436. ----------
  437. %(input)s
  438. shift : float or sequence
  439. The shift along the axes. If a float, `shift` is the same for each
  440. axis. If a sequence, `shift` should contain one value for each axis.
  441. %(output)s
  442. order : int, optional
  443. The order of the spline interpolation, default is 3.
  444. The order has to be in the range 0-5.
  445. %(mode)s
  446. %(cval)s
  447. %(prefilter)s
  448. Returns
  449. -------
  450. shift : ndarray
  451. The shifted input.
  452. """
  453. if order < 0 or order > 5:
  454. raise RuntimeError('spline order not supported')
  455. input = numpy.asarray(input)
  456. if numpy.iscomplexobj(input):
  457. raise TypeError('Complex type not supported')
  458. if input.ndim < 1:
  459. raise RuntimeError('input and output rank must be > 0')
  460. mode = _ni_support._extend_mode_to_code(mode)
  461. if prefilter and order > 1:
  462. filtered = spline_filter(input, order, output=numpy.float64)
  463. else:
  464. filtered = input
  465. output = _ni_support._get_output(output, input)
  466. shift = _ni_support._normalize_sequence(shift, input.ndim)
  467. shift = [-ii for ii in shift]
  468. shift = numpy.asarray(shift, dtype=numpy.float64)
  469. if not shift.flags.contiguous:
  470. shift = shift.copy()
  471. _nd_image.zoom_shift(filtered, None, shift, output, order, mode, cval)
  472. return output
  473. @docfiller
  474. def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0,
  475. prefilter=True):
  476. """
  477. Zoom an array.
  478. The array is zoomed using spline interpolation of the requested order.
  479. Parameters
  480. ----------
  481. %(input)s
  482. zoom : float or sequence
  483. The zoom factor along the axes. If a float, `zoom` is the same for each
  484. axis. If a sequence, `zoom` should contain one value for each axis.
  485. %(output)s
  486. order : int, optional
  487. The order of the spline interpolation, default is 3.
  488. The order has to be in the range 0-5.
  489. %(mode)s
  490. %(cval)s
  491. %(prefilter)s
  492. Returns
  493. -------
  494. zoom : ndarray
  495. The zoomed input.
  496. Examples
  497. --------
  498. >>> from scipy import ndimage, misc
  499. >>> import matplotlib.pyplot as plt
  500. >>> fig = plt.figure()
  501. >>> ax1 = fig.add_subplot(121) # left side
  502. >>> ax2 = fig.add_subplot(122) # right side
  503. >>> ascent = misc.ascent()
  504. >>> result = ndimage.zoom(ascent, 3.0)
  505. >>> ax1.imshow(ascent)
  506. >>> ax2.imshow(result)
  507. >>> plt.show()
  508. >>> print(ascent.shape)
  509. (512, 512)
  510. >>> print(result.shape)
  511. (1536, 1536)
  512. """
  513. if order < 0 or order > 5:
  514. raise RuntimeError('spline order not supported')
  515. input = numpy.asarray(input)
  516. if numpy.iscomplexobj(input):
  517. raise TypeError('Complex type not supported')
  518. if input.ndim < 1:
  519. raise RuntimeError('input and output rank must be > 0')
  520. mode = _ni_support._extend_mode_to_code(mode)
  521. if prefilter and order > 1:
  522. filtered = spline_filter(input, order, output=numpy.float64)
  523. else:
  524. filtered = input
  525. zoom = _ni_support._normalize_sequence(zoom, input.ndim)
  526. output_shape = tuple(
  527. [int(round(ii * jj)) for ii, jj in zip(input.shape, zoom)])
  528. output_shape_old = tuple(
  529. [int(ii * jj) for ii, jj in zip(input.shape, zoom)])
  530. if output_shape != output_shape_old:
  531. warnings.warn(
  532. "From scipy 0.13.0, the output shape of zoom() is calculated "
  533. "with round() instead of int() - for these inputs the size of "
  534. "the returned array has changed.", UserWarning)
  535. zoom_div = numpy.array(output_shape, float) - 1
  536. # Zooming to infinite values is unpredictable, so just choose
  537. # zoom factor 1 instead
  538. zoom = numpy.divide(numpy.array(input.shape) - 1, zoom_div,
  539. out=numpy.ones_like(input.shape, dtype=numpy.float64),
  540. where=zoom_div != 0)
  541. output = _ni_support._get_output(output, input,
  542. shape=output_shape)
  543. zoom = numpy.ascontiguousarray(zoom)
  544. _nd_image.zoom_shift(filtered, zoom, None, output, order, mode, cval)
  545. return output
  546. def _minmax(coor, minc, maxc):
  547. if coor[0] < minc[0]:
  548. minc[0] = coor[0]
  549. if coor[0] > maxc[0]:
  550. maxc[0] = coor[0]
  551. if coor[1] < minc[1]:
  552. minc[1] = coor[1]
  553. if coor[1] > maxc[1]:
  554. maxc[1] = coor[1]
  555. return minc, maxc
  556. @docfiller
  557. def rotate(input, angle, axes=(1, 0), reshape=True, output=None, order=3,
  558. mode='constant', cval=0.0, prefilter=True):
  559. """
  560. Rotate an array.
  561. The array is rotated in the plane defined by the two axes given by the
  562. `axes` parameter using spline interpolation of the requested order.
  563. Parameters
  564. ----------
  565. %(input)s
  566. angle : float
  567. The rotation angle in degrees.
  568. axes : tuple of 2 ints, optional
  569. The two axes that define the plane of rotation. Default is the first
  570. two axes.
  571. reshape : bool, optional
  572. If `reshape` is true, the output shape is adapted so that the input
  573. array is contained completely in the output. Default is True.
  574. %(output)s
  575. order : int, optional
  576. The order of the spline interpolation, default is 3.
  577. The order has to be in the range 0-5.
  578. %(mode)s
  579. %(cval)s
  580. %(prefilter)s
  581. Returns
  582. -------
  583. rotate : ndarray
  584. The rotated input.
  585. """
  586. input = numpy.asarray(input)
  587. axes = list(axes)
  588. rank = input.ndim
  589. if axes[0] < 0:
  590. axes[0] += rank
  591. if axes[1] < 0:
  592. axes[1] += rank
  593. if axes[0] < 0 or axes[1] < 0 or axes[0] > rank or axes[1] > rank:
  594. raise RuntimeError('invalid rotation plane specified')
  595. if axes[0] > axes[1]:
  596. axes = axes[1], axes[0]
  597. angle = numpy.pi / 180 * angle
  598. m11 = math.cos(angle)
  599. m12 = math.sin(angle)
  600. m21 = -math.sin(angle)
  601. m22 = math.cos(angle)
  602. matrix = numpy.array([[m11, m12],
  603. [m21, m22]], dtype=numpy.float64)
  604. iy = input.shape[axes[0]]
  605. ix = input.shape[axes[1]]
  606. if reshape:
  607. mtrx = numpy.array([[m11, -m21],
  608. [-m12, m22]], dtype=numpy.float64)
  609. minc = [0, 0]
  610. maxc = [0, 0]
  611. coor = numpy.dot(mtrx, [0, ix])
  612. minc, maxc = _minmax(coor, minc, maxc)
  613. coor = numpy.dot(mtrx, [iy, 0])
  614. minc, maxc = _minmax(coor, minc, maxc)
  615. coor = numpy.dot(mtrx, [iy, ix])
  616. minc, maxc = _minmax(coor, minc, maxc)
  617. oy = int(maxc[0] - minc[0] + 0.5)
  618. ox = int(maxc[1] - minc[1] + 0.5)
  619. else:
  620. oy = input.shape[axes[0]]
  621. ox = input.shape[axes[1]]
  622. offset = numpy.zeros((2,), dtype=numpy.float64)
  623. offset[0] = float(oy) / 2.0 - 0.5
  624. offset[1] = float(ox) / 2.0 - 0.5
  625. offset = numpy.dot(matrix, offset)
  626. tmp = numpy.zeros((2,), dtype=numpy.float64)
  627. tmp[0] = float(iy) / 2.0 - 0.5
  628. tmp[1] = float(ix) / 2.0 - 0.5
  629. offset = tmp - offset
  630. output_shape = list(input.shape)
  631. output_shape[axes[0]] = oy
  632. output_shape[axes[1]] = ox
  633. output_shape = tuple(output_shape)
  634. output = _ni_support._get_output(output, input,
  635. shape=output_shape)
  636. if input.ndim <= 2:
  637. affine_transform(input, matrix, offset, output_shape, output,
  638. order, mode, cval, prefilter)
  639. else:
  640. coordinates = []
  641. size = numpy.product(input.shape, axis=0)
  642. size //= input.shape[axes[0]]
  643. size //= input.shape[axes[1]]
  644. for ii in range(input.ndim):
  645. if ii not in axes:
  646. coordinates.append(0)
  647. else:
  648. coordinates.append(slice(None, None, None))
  649. iter_axes = list(range(input.ndim))
  650. iter_axes.reverse()
  651. iter_axes.remove(axes[0])
  652. iter_axes.remove(axes[1])
  653. os = (output_shape[axes[0]], output_shape[axes[1]])
  654. for ii in range(size):
  655. ia = input[tuple(coordinates)]
  656. oa = output[tuple(coordinates)]
  657. affine_transform(ia, matrix, offset, os, oa, order, mode,
  658. cval, prefilter)
  659. for jj in iter_axes:
  660. if coordinates[jj] < input.shape[jj] - 1:
  661. coordinates[jj] += 1
  662. break
  663. else:
  664. coordinates[jj] = 0
  665. return output