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

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 scipy import special 

38from . import _ni_support 

39from . import _nd_image 

40from ._ni_docstrings import docfiller 

41 

42 

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

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

45 

46 

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. 

52 

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. 

55 

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 

68 

69 Returns 

70 ------- 

71 spline_filter1d : ndarray 

72 The filtered input. 

73 

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. 

85 

86 For complex-valued `input`, this function processes the real and imaginary 

87 components independently. 

88 

89 .. versionadded:: 1.6.0 

90 Complex-valued support added. 

91 

92 See Also 

93 -------- 

94 spline_filter : Multidimensional spline filter. 

95 

96 Examples 

97 -------- 

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

99 

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

115 

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 

134 

135 

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

137 """ 

138 Multidimensional spline filter. 

139 

140 For more details, see `spline_filter1d`. 

141 

142 See Also 

143 -------- 

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

145 

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. 

153 

154 For complex-valued `input`, this function processes the real and imaginary 

155 components independently. 

156 

157 .. versionadded:: 1.6.0 

158 Complex-valued support added. 

159 

160 Examples 

161 -------- 

162 We can filter an image using multidimentional splines: 

163 

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

177 

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 

196 

197 

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 

212 

213 

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. 

221 

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. 

226 

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

247 

248 Returns 

249 ------- 

250 output : ndarray 

251 The filtered input. 

252 

253 See Also 

254 -------- 

255 map_coordinates, affine_transform, spline_filter1d 

256 

257 

258 Notes 

259 ----- 

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

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

262 

263 .. code:: c 

264 

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) 

269 

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. 

278 

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. 

284 

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. 

288 

289 For complex-valued `input`, this function transforms the real and imaginary 

290 components independently. 

291 

292 .. versionadded:: 1.6.0 

293 Complex-valued support added. 

294 

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

308 

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

321 

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 

343 

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 

356 

357 

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. 

363 

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. 

368 

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. 

373 

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 

386 

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. 

392 

393 See Also 

394 -------- 

395 spline_filter, geometric_transform, scipy.interpolate 

396 

397 Notes 

398 ----- 

399 For complex-valued `input`, this function maps the real and imaginary 

400 components independently. 

401 

402 .. versionadded:: 1.6.0 

403 Complex-valued support added. 

404 

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

417 

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

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

420 

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) 

428 

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 

462 

463 

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. 

470 

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

474 

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. 

480 

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: 

489 

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. 

502 

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 

516 

517 Returns 

518 ------- 

519 affine_transform : ndarray 

520 The transformed input. 

521 

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. 

530 

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

538 

539 For complex-valued `input`, this function transforms the real and imaginary 

540 components independently. 

541 

542 .. versionadded:: 1.6.0 

543 Complex-valued support added. 

544 

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 

618 

619 

620@docfiller 

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

622 prefilter=True): 

623 """ 

624 Shift an array. 

625 

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. 

629 

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 

643 

644 Returns 

645 ------- 

646 shift : ndarray 

647 The shifted input. 

648 

649 Notes 

650 ----- 

651 For complex-valued `input`, this function shifts the real and imaginary 

652 components independently. 

653 

654 .. versionadded:: 1.6.0 

655 Complex-valued support added. 

656 

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 

669 

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 

692 

693 

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. 

699 

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

701 

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: 

721 

722 .. code-block:: text 

723 

724 | pixel 1 | pixel 2 | pixel 3 | pixel 4 | pixel 5 | 

725 |<-------------------------------------->| 

726 vs. 

727 |<----------------------------------------------->| 

728 

729 The starting point of the arrow in the diagram above corresponds to 

730 coordinate location 0 in each mode. 

731 

732 Returns 

733 ------- 

734 zoom : ndarray 

735 The zoomed input. 

736 

737 Notes 

738 ----- 

739 For complex-valued `input`, this function zooms the real and imaginary 

740 components independently. 

741 

742 .. versionadded:: 1.6.0 

743 Complex-valued support added. 

744 

745 Examples 

746 -------- 

747 >>> from scipy import ndimage, datasets 

748 >>> import matplotlib.pyplot as plt 

749 

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

758 

759 >>> print(ascent.shape) 

760 (512, 512) 

761 

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 

779 

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) 

806 

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 

812 

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 

822 

823 

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. 

829 

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. 

832 

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 

851 

852 Returns 

853 ------- 

854 rotate : ndarray 

855 The rotated input. 

856 

857 Notes 

858 ----- 

859 For complex-valued `input`, this function rotates the real and imaginary 

860 components independently. 

861 

862 .. versionadded:: 1.6.0 

863 Complex-valued support added. 

864 

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) 

888 

889 """ 

890 input_arr = numpy.asarray(input) 

891 ndim = input_arr.ndim 

892 

893 if ndim < 2: 

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

895 

896 axes = list(axes) 

897 

898 if len(axes) != 2: 

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

900 

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

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

903 

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

910 

911 axes.sort() 

912 

913 c, s = special.cosdg(angle), special.sindg(angle) 

914 

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

916 [-s, c]]) 

917 

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] 

929 

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

931 in_center = (in_plane_shape - 1) / 2 

932 offset = in_center - out_center 

933 

934 output_shape = img_shape 

935 output_shape[axes] = out_plane_shape 

936 output_shape = tuple(output_shape) 

937 

938 complex_output = numpy.iscomplexobj(input_arr) 

939 output = _ni_support._get_output(output, input_arr, shape=output_shape, 

940 complex_output=complex_output) 

941 

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

951 

952 out_plane_shape = tuple(out_plane_shape) 

953 

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) 

959 

960 return output