Hide keyboard shortcuts

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. 

30 

31import itertools 

32import warnings 

33 

34import numpy 

35from numpy.core.multiarray import normalize_axis_index 

36 

37from . import _ni_support 

38from . import _nd_image 

39from ._ni_docstrings import docdict 

40from scipy._lib import doccer 

41 

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'") 

47 

48docfiller = doccer.filldoc(docdict_copy) 

49 

50__all__ = ['spline_filter1d', 'spline_filter', 'geometric_transform', 

51 'map_coordinates', 'affine_transform', 'shift', 'zoom', 'rotate'] 

52 

53 

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. 

59 

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. 

62 

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 

75 

76 Returns 

77 ------- 

78 spline_filter1d : ndarray 

79 The filtered input. 

80 

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. 

92 

93 See Also 

94 -------- 

95 spline_filter : Multidimensional spline filter. 

96 

97 Examples 

98 -------- 

99 We can filter an image using 1-D spline along the given axis: 

100 

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() 

115 

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 

130 

131 

132def spline_filter(input, order=3, output=numpy.float64, mode='mirror'): 

133 """ 

134 Multidimensional spline filter. 

135 

136 For more details, see `spline_filter1d`. 

137 

138 See Also 

139 -------- 

140 spline_filter1d : Calculate a 1-D spline filter along the given axis. 

141 

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. 

149 

150 Examples 

151 -------- 

152 We can filter an image using multidimentional splines: 

153 

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() 

166 

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 

181 

182 

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. 

190 

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. 

195 

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`. 

216 

217 Returns 

218 ------- 

219 output : ndarray 

220 The filtered input. 

221 

222 See Also 

223 -------- 

224 map_coordinates, affine_transform, spline_filter1d 

225 

226 

227 Notes 

228 ----- 

229 This function also accepts low-level callback functions with one 

230 the following signatures and wrapped in `scipy.LowLevelCallable`: 

231 

232 .. code:: c 

233 

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) 

238 

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. 

247 

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. 

253 

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. 

257 

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]]) 

271 

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]) 

284 

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 

305 

306 

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. 

312 

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. 

317 

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. 

322 

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 

335 

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. 

341 

342 See Also 

343 -------- 

344 spline_filter, geometric_transform, scipy.interpolate 

345 

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.]) 

357 

358 Above, the interpolated value of a[0.5, 0.5] gives output[0], while 

359 a[2, 1] is output[1]. 

360 

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) 

368 

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 

393 

394 

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. 

401 

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``. 

405 

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. 

411 

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: 

420 

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. 

433 

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 

447 

448 Returns 

449 ------- 

450 affine_transform : ndarray 

451 The transformed input. 

452 

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. 

461 

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)``. 

469 

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 

529 

530 

531@docfiller 

532def shift(input, shift, output=None, order=3, mode='constant', cval=0.0, 

533 prefilter=True): 

534 """ 

535 Shift an array. 

536 

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. 

540 

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 

554 

555 Returns 

556 ------- 

557 shift : ndarray 

558 The shifted input. 

559 

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 

581 

582 

583@docfiller 

584def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0, 

585 prefilter=True): 

586 """ 

587 Zoom an array. 

588 

589 The array is zoomed using spline interpolation of the requested order. 

590 

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 

604 

605 Returns 

606 ------- 

607 zoom : ndarray 

608 The zoomed input. 

609 

610 Examples 

611 -------- 

612 >>> from scipy import ndimage, misc 

613 >>> import matplotlib.pyplot as plt 

614 

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() 

623 

624 >>> print(ascent.shape) 

625 (512, 512) 

626 

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)]) 

645 

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) 

652 

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 

658 

659 

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. 

665 

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. 

668 

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 

687 

688 Returns 

689 ------- 

690 rotate : ndarray 

691 The rotated input. 

692 

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) 

716 

717 """ 

718 input_arr = numpy.asarray(input) 

719 ndim = input_arr.ndim 

720 

721 if ndim < 2: 

722 raise ValueError('input array should be at least 2D') 

723 

724 axes = list(axes) 

725 

726 if len(axes) != 2: 

727 raise ValueError('axes should contain exactly two values') 

728 

729 if not all([float(ax).is_integer() for ax in axes]): 

730 raise ValueError('axes should contain only integer values') 

731 

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') 

738 

739 axes.sort() 

740 

741 angle_rad = numpy.deg2rad(angle) 

742 c, s = numpy.cos(angle_rad), numpy.sin(angle_rad) 

743 

744 rot_matrix = numpy.array([[c, s], 

745 [-s, c]]) 

746 

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] 

758 

759 out_center = rot_matrix @ ((out_plane_shape - 1) / 2) 

760 in_center = (in_plane_shape - 1) / 2 

761 offset = in_center - out_center 

762 

763 output_shape = img_shape 

764 output_shape[axes] = out_plane_shape 

765 output_shape = tuple(output_shape) 

766 

767 output = _ni_support._get_output(output, input_arr, shape=output_shape) 

768 

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)]) 

778 

779 out_plane_shape = tuple(out_plane_shape) 

780 

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) 

786 

787 return output