1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

# Copyright (C) 2003-2005 Peter J. Verveer 

# 

# Redistribution and use in source and binary forms, with or without 

# modification, are permitted provided that the following conditions 

# are met: 

# 

# 1. Redistributions of source code must retain the above copyright 

# notice, this list of conditions and the following disclaimer. 

# 

# 2. Redistributions in binary form must reproduce the above 

# copyright notice, this list of conditions and the following 

# disclaimer in the documentation and/or other materials provided 

# with the distribution. 

# 

# 3. The name of the author may not be used to endorse or promote 

# products derived from this software without specific prior 

# written permission. 

# 

# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 

# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 

# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 

# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 

# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 

# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 

# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 

# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 

# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 

# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

 

from __future__ import division, print_function, absolute_import 

 

import math 

import numpy 

from . import _ni_support 

from . import _nd_image 

from . import _ni_docstrings 

from functools import wraps 

 

import warnings 

 

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

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

 

 

@_ni_docstrings.docfiller 

def spline_filter1d(input, order=3, axis=-1, output=numpy.float64): 

""" 

Calculate a one-dimensional spline filter along the given axis. 

 

The lines of the array along the given axis are filtered by a 

spline filter. The order of the spline must be >= 2 and <= 5. 

 

Parameters 

---------- 

%(input)s 

order : int, optional 

The order of the spline, default is 3. 

axis : int, optional 

The axis along which the spline filter is applied. Default is the last 

axis. 

output : ndarray or dtype, optional 

The array in which to place the output, or the dtype of the returned 

array. Default is `numpy.float64`. 

 

Returns 

------- 

spline_filter1d : ndarray 

The filtered input. 

 

""" 

if order < 0 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

output = _ni_support._get_output(output, input) 

if order in [0, 1]: 

output[...] = numpy.array(input) 

else: 

axis = _ni_support._check_axis(axis, input.ndim) 

_nd_image.spline_filter1d(input, order, axis, output) 

return output 

 

 

def spline_filter(input, order=3, output=numpy.float64): 

""" 

Multi-dimensional spline filter. 

 

For more details, see `spline_filter1d`. 

 

See Also 

-------- 

spline_filter1d 

 

Notes 

----- 

The multi-dimensional filter is implemented as a sequence of 

one-dimensional spline filters. The intermediate arrays are stored 

in the same data type as the output. Therefore, for output types 

with a limited precision, the results may be imprecise because 

intermediate results may be stored with insufficient precision. 

 

""" 

if order < 2 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

output = _ni_support._get_output(output, input) 

if order not in [0, 1] and input.ndim > 0: 

for axis in range(input.ndim): 

spline_filter1d(input, order, axis, output=output) 

input = output 

else: 

output[...] = input[...] 

return output 

 

 

@_ni_docstrings.docfiller 

def geometric_transform(input, mapping, output_shape=None, 

output=None, order=3, 

mode='constant', cval=0.0, prefilter=True, 

extra_arguments=(), extra_keywords={}): 

""" 

Apply an arbitrary geometric transform. 

 

The given mapping function is used to find, for each point in the 

output, the corresponding coordinates in the input. The value of the 

input at those coordinates is determined by spline interpolation of 

the requested order. 

 

Parameters 

---------- 

%(input)s 

mapping : {callable, scipy.LowLevelCallable} 

A callable object that accepts a tuple of length equal to the output 

array rank, and returns the corresponding input coordinates as a tuple 

of length equal to the input array rank. 

output_shape : tuple of ints, optional 

Shape tuple. 

%(output)s 

order : int, optional 

The order of the spline interpolation, default is 3. 

The order has to be in the range 0-5. 

%(mode)s 

%(cval)s 

%(prefilter)s 

extra_arguments : tuple, optional 

Extra arguments passed to `mapping`. 

extra_keywords : dict, optional 

Extra keywords passed to `mapping`. 

 

Returns 

------- 

output : ndarray 

The filtered input. 

 

See Also 

-------- 

map_coordinates, affine_transform, spline_filter1d 

 

 

Notes 

----- 

This function also accepts low-level callback functions with one 

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

 

.. code:: c 

 

int mapping(npy_intp *output_coordinates, double *input_coordinates, 

int output_rank, int input_rank, void *user_data) 

int mapping(intptr_t *output_coordinates, double *input_coordinates, 

int output_rank, int input_rank, void *user_data) 

 

The calling function iterates over the elements of the output array, 

calling the callback function at each element. The coordinates of the 

current output element are passed through ``output_coordinates``. The 

callback function must return the coordinates at which the input must 

be interpolated in ``input_coordinates``. The rank of the input and 

output arrays are given by ``input_rank`` and ``output_rank`` 

respectively. ``user_data`` is the data pointer provided 

to `scipy.LowLevelCallable` as-is. 

 

The callback function must return an integer error status that is zero 

if something went wrong and one otherwise. If an error occurs, you should 

normally set the python error status with an informative message 

before returning, otherwise a default error message is set by the 

calling function. 

 

In addition, some other low-level function pointer specifications 

are accepted, but these are for backward compatibility only and should 

not be used in new code. 

 

Examples 

-------- 

>>> import numpy as np 

>>> from scipy.ndimage import geometric_transform 

>>> a = np.arange(12.).reshape((4, 3)) 

>>> def shift_func(output_coords): 

... return (output_coords[0] - 0.5, output_coords[1] - 0.5) 

... 

>>> geometric_transform(a, shift_func) 

array([[ 0. , 0. , 0. ], 

[ 0. , 1.362, 2.738], 

[ 0. , 4.812, 6.187], 

[ 0. , 8.263, 9.637]]) 

 

>>> b = [1, 2, 3, 4, 5] 

>>> def shift_func(output_coords): 

... return (output_coords[0] - 3,) 

... 

>>> geometric_transform(b, shift_func, mode='constant') 

array([0, 0, 0, 1, 2]) 

>>> geometric_transform(b, shift_func, mode='nearest') 

array([1, 1, 1, 1, 2]) 

>>> geometric_transform(b, shift_func, mode='reflect') 

array([3, 2, 1, 1, 2]) 

>>> geometric_transform(b, shift_func, mode='wrap') 

array([2, 3, 4, 1, 2]) 

 

""" 

if order < 0 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

if output_shape is None: 

output_shape = input.shape 

if input.ndim < 1 or len(output_shape) < 1: 

raise RuntimeError('input and output rank must be > 0') 

mode = _ni_support._extend_mode_to_code(mode) 

if prefilter and order > 1: 

filtered = spline_filter(input, order, output=numpy.float64) 

else: 

filtered = input 

output = _ni_support._get_output(output, input, shape=output_shape) 

_nd_image.geometric_transform(filtered, mapping, None, None, None, output, 

order, mode, cval, extra_arguments, 

extra_keywords) 

return output 

 

 

@_ni_docstrings.docfiller 

def map_coordinates(input, coordinates, output=None, order=3, 

mode='constant', cval=0.0, prefilter=True): 

""" 

Map the input array to new coordinates by interpolation. 

 

The array of coordinates is used to find, for each point in the output, 

the corresponding coordinates in the input. The value of the input at 

those coordinates is determined by spline interpolation of the 

requested order. 

 

The shape of the output is derived from that of the coordinate 

array by dropping the first axis. The values of the array along 

the first axis are the coordinates in the input array at which the 

output value is found. 

 

Parameters 

---------- 

%(input)s 

coordinates : array_like 

The coordinates at which `input` is evaluated. 

%(output)s 

order : int, optional 

The order of the spline interpolation, default is 3. 

The order has to be in the range 0-5. 

%(mode)s 

%(cval)s 

%(prefilter)s 

 

Returns 

------- 

map_coordinates : ndarray 

The result of transforming the input. The shape of the output is 

derived from that of `coordinates` by dropping the first axis. 

 

See Also 

-------- 

spline_filter, geometric_transform, scipy.interpolate 

 

Examples 

-------- 

>>> from scipy import ndimage 

>>> a = np.arange(12.).reshape((4, 3)) 

>>> a 

array([[ 0., 1., 2.], 

[ 3., 4., 5.], 

[ 6., 7., 8.], 

[ 9., 10., 11.]]) 

>>> ndimage.map_coordinates(a, [[0.5, 2], [0.5, 1]], order=1) 

array([ 2., 7.]) 

 

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

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

 

>>> inds = np.array([[0.5, 2], [0.5, 4]]) 

>>> ndimage.map_coordinates(a, inds, order=1, cval=-33.3) 

array([ 2. , -33.3]) 

>>> ndimage.map_coordinates(a, inds, order=1, mode='nearest') 

array([ 2., 8.]) 

>>> ndimage.map_coordinates(a, inds, order=1, cval=0, output=bool) 

array([ True, False], dtype=bool) 

 

""" 

if order < 0 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

coordinates = numpy.asarray(coordinates) 

if numpy.iscomplexobj(coordinates): 

raise TypeError('Complex type not supported') 

output_shape = coordinates.shape[1:] 

if input.ndim < 1 or len(output_shape) < 1: 

raise RuntimeError('input and output rank must be > 0') 

if coordinates.shape[0] != input.ndim: 

raise RuntimeError('invalid shape for coordinate array') 

mode = _ni_support._extend_mode_to_code(mode) 

if prefilter and order > 1: 

filtered = spline_filter(input, order, output=numpy.float64) 

else: 

filtered = input 

output = _ni_support._get_output(output, input, 

shape=output_shape) 

_nd_image.geometric_transform(filtered, None, coordinates, None, None, 

output, order, mode, cval, None, None) 

return output 

 

 

@_ni_docstrings.docfiller 

def affine_transform(input, matrix, offset=0.0, output_shape=None, 

output=None, order=3, 

mode='constant', cval=0.0, prefilter=True): 

""" 

Apply an affine transformation. 

 

Given an output image pixel index vector ``o``, the pixel value 

is determined from the input image at position 

``np.dot(matrix, o) + offset``. 

 

Parameters 

---------- 

%(input)s 

matrix : ndarray 

The inverse coordinate transformation matrix, mapping output 

coordinates to input coordinates. If ``ndim`` is the number of 

dimensions of ``input``, the given matrix must have one of the 

following shapes: 

 

- ``(ndim, ndim)``: the linear transformation matrix for each 

output coordinate. 

- ``(ndim,)``: assume that the 2D transformation matrix is 

diagonal, with the diagonal specified by the given value. A more 

efficient algorithm is then used that exploits the separability 

of the problem. 

- ``(ndim + 1, ndim + 1)``: assume that the transformation is 

specified using homogeneous coordinates [1]_. In this case, any 

value passed to ``offset`` is ignored. 

- ``(ndim, ndim + 1)``: as above, but the bottom row of a 

homogeneous transformation matrix is always ``[0, 0, ..., 1]``, 

and may be omitted. 

 

offset : float or sequence, optional 

The offset into the array where the transform is applied. If a float, 

`offset` is the same for each axis. If a sequence, `offset` should 

contain one value for each axis. 

output_shape : tuple of ints, optional 

Shape tuple. 

%(output)s 

order : int, optional 

The order of the spline interpolation, default is 3. 

The order has to be in the range 0-5. 

%(mode)s 

%(cval)s 

%(prefilter)s 

 

Returns 

------- 

affine_transform : ndarray 

The transformed input. 

 

Notes 

----- 

The given matrix and offset are used to find for each point in the 

output the corresponding coordinates in the input by an affine 

transformation. The value of the input at those coordinates is 

determined by spline interpolation of the requested order. Points 

outside the boundaries of the input are filled according to the given 

mode. 

 

.. versionchanged:: 0.18.0 

Previously, the exact interpretation of the affine transformation 

depended on whether the matrix was supplied as a one-dimensional or 

two-dimensional array. If a one-dimensional array was supplied 

to the matrix parameter, the output pixel value at index ``o`` 

was determined from the input image at position 

``matrix * (o + offset)``. 

 

References 

---------- 

.. [1] https://en.wikipedia.org/wiki/Homogeneous_coordinates 

""" 

if order < 0 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

if output_shape is None: 

output_shape = input.shape 

if input.ndim < 1 or len(output_shape) < 1: 

raise RuntimeError('input and output rank must be > 0') 

mode = _ni_support._extend_mode_to_code(mode) 

if prefilter and order > 1: 

filtered = spline_filter(input, order, output=numpy.float64) 

else: 

filtered = input 

output = _ni_support._get_output(output, input, 

shape=output_shape) 

matrix = numpy.asarray(matrix, dtype=numpy.float64) 

if matrix.ndim not in [1, 2] or matrix.shape[0] < 1: 

raise RuntimeError('no proper affine matrix provided') 

if (matrix.ndim == 2 and matrix.shape[1] == input.ndim + 1 and 

(matrix.shape[0] in [input.ndim, input.ndim + 1])): 

if matrix.shape[0] == input.ndim + 1: 

exptd = [0] * input.ndim + [1] 

if not numpy.all(matrix[input.ndim] == exptd): 

msg = ('Expected homogeneous transformation matrix with ' 

'shape %s for image shape %s, but bottom row was ' 

'not equal to %s' % (matrix.shape, input.shape, exptd)) 

raise ValueError(msg) 

# assume input is homogeneous coordinate transformation matrix 

offset = matrix[:input.ndim, input.ndim] 

matrix = matrix[:input.ndim, :input.ndim] 

if matrix.shape[0] != input.ndim: 

raise RuntimeError('affine matrix has wrong number of rows') 

if matrix.ndim == 2 and matrix.shape[1] != output.ndim: 

raise RuntimeError('affine matrix has wrong number of columns') 

if not matrix.flags.contiguous: 

matrix = matrix.copy() 

offset = _ni_support._normalize_sequence(offset, input.ndim) 

offset = numpy.asarray(offset, dtype=numpy.float64) 

if offset.ndim != 1 or offset.shape[0] < 1: 

raise RuntimeError('no proper offset provided') 

if not offset.flags.contiguous: 

offset = offset.copy() 

if matrix.ndim == 1: 

warnings.warn( 

"The behaviour of affine_transform with a one-dimensional " 

"array supplied for the matrix parameter has changed in " 

"scipy 0.18.0." 

) 

_nd_image.zoom_shift(filtered, matrix, offset/matrix, output, order, 

mode, cval) 

else: 

_nd_image.geometric_transform(filtered, None, None, matrix, offset, 

output, order, mode, cval, None, None) 

return output 

 

 

@_ni_docstrings.docfiller 

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

prefilter=True): 

""" 

Shift an array. 

 

The array is shifted using spline interpolation of the requested order. 

Points outside the boundaries of the input are filled according to the 

given mode. 

 

Parameters 

---------- 

%(input)s 

shift : float or sequence 

The shift along the axes. If a float, `shift` is the same for each 

axis. If a sequence, `shift` should contain one value for each axis. 

%(output)s 

order : int, optional 

The order of the spline interpolation, default is 3. 

The order has to be in the range 0-5. 

%(mode)s 

%(cval)s 

%(prefilter)s 

 

Returns 

------- 

shift : ndarray 

The shifted input. 

 

""" 

if order < 0 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

if input.ndim < 1: 

raise RuntimeError('input and output rank must be > 0') 

mode = _ni_support._extend_mode_to_code(mode) 

if prefilter and order > 1: 

filtered = spline_filter(input, order, output=numpy.float64) 

else: 

filtered = input 

output = _ni_support._get_output(output, input) 

shift = _ni_support._normalize_sequence(shift, input.ndim) 

shift = [-ii for ii in shift] 

shift = numpy.asarray(shift, dtype=numpy.float64) 

if not shift.flags.contiguous: 

shift = shift.copy() 

_nd_image.zoom_shift(filtered, None, shift, output, order, mode, cval) 

return output 

 

 

@_ni_docstrings.docfiller 

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

prefilter=True): 

""" 

Zoom an array. 

 

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

 

Parameters 

---------- 

%(input)s 

zoom : float or sequence 

The zoom factor along the axes. If a float, `zoom` is the same for each 

axis. If a sequence, `zoom` should contain one value for each axis. 

%(output)s 

order : int, optional 

The order of the spline interpolation, default is 3. 

The order has to be in the range 0-5. 

%(mode)s 

%(cval)s 

%(prefilter)s 

 

Returns 

------- 

zoom : ndarray 

The zoomed input. 

 

Examples 

-------- 

>>> from scipy import ndimage, misc 

>>> import matplotlib.pyplot as plt 

 

>>> fig = plt.figure() 

>>> ax1 = fig.add_subplot(121) # left side 

>>> ax2 = fig.add_subplot(122) # right side 

>>> ascent = misc.ascent() 

>>> result = ndimage.zoom(ascent, 3.0) 

>>> ax1.imshow(ascent) 

>>> ax2.imshow(result) 

>>> plt.show() 

 

>>> print(ascent.shape) 

(512, 512) 

 

>>> print(result.shape) 

(1536, 1536) 

""" 

if order < 0 or order > 5: 

raise RuntimeError('spline order not supported') 

input = numpy.asarray(input) 

if numpy.iscomplexobj(input): 

raise TypeError('Complex type not supported') 

if input.ndim < 1: 

raise RuntimeError('input and output rank must be > 0') 

mode = _ni_support._extend_mode_to_code(mode) 

if prefilter and order > 1: 

filtered = spline_filter(input, order, output=numpy.float64) 

else: 

filtered = input 

zoom = _ni_support._normalize_sequence(zoom, input.ndim) 

output_shape = tuple( 

[int(round(ii * jj)) for ii, jj in zip(input.shape, zoom)]) 

 

output_shape_old = tuple( 

[int(ii * jj) for ii, jj in zip(input.shape, zoom)]) 

if output_shape != output_shape_old: 

warnings.warn( 

"From scipy 0.13.0, the output shape of zoom() is calculated " 

"with round() instead of int() - for these inputs the size of " 

"the returned array has changed.", UserWarning) 

 

zoom_div = numpy.array(output_shape, float) - 1 

# Zooming to infinite values is unpredictable, so just choose 

# zoom factor 1 instead 

zoom = numpy.divide(numpy.array(input.shape) - 1, zoom_div, 

out=numpy.ones_like(input.shape, dtype=numpy.float64), 

where=zoom_div != 0) 

 

output = _ni_support._get_output(output, input, 

shape=output_shape) 

zoom = numpy.ascontiguousarray(zoom) 

_nd_image.zoom_shift(filtered, zoom, None, output, order, mode, cval) 

return output 

 

 

def _minmax(coor, minc, maxc): 

if coor[0] < minc[0]: 

minc[0] = coor[0] 

if coor[0] > maxc[0]: 

maxc[0] = coor[0] 

if coor[1] < minc[1]: 

minc[1] = coor[1] 

if coor[1] > maxc[1]: 

maxc[1] = coor[1] 

return minc, maxc 

 

 

def rotate(input, angle, axes=(1, 0), reshape=True, output=None, order=3, 

mode='constant', cval=0.0, prefilter=True): 

""" 

Rotate an array. 

 

The array is rotated in the plane defined by the two axes given by the 

`axes` parameter using spline interpolation of the requested order. 

 

Parameters 

---------- 

%(input)s 

angle : float 

The rotation angle in degrees. 

axes : tuple of 2 ints, optional 

The two axes that define the plane of rotation. Default is the first 

two axes. 

reshape : bool, optional 

If `reshape` is true, the output shape is adapted so that the input 

array is contained completely in the output. Default is True. 

%(output)s 

order : int, optional 

The order of the spline interpolation, default is 3. 

The order has to be in the range 0-5. 

%(mode)s 

%(cval)s 

%(prefilter)s 

 

Returns 

------- 

rotate : ndarray 

The rotated input. 

 

""" 

input = numpy.asarray(input) 

axes = list(axes) 

rank = input.ndim 

if axes[0] < 0: 

axes[0] += rank 

if axes[1] < 0: 

axes[1] += rank 

if axes[0] < 0 or axes[1] < 0 or axes[0] > rank or axes[1] > rank: 

raise RuntimeError('invalid rotation plane specified') 

if axes[0] > axes[1]: 

axes = axes[1], axes[0] 

angle = numpy.pi / 180 * angle 

m11 = math.cos(angle) 

m12 = math.sin(angle) 

m21 = -math.sin(angle) 

m22 = math.cos(angle) 

matrix = numpy.array([[m11, m12], 

[m21, m22]], dtype=numpy.float64) 

iy = input.shape[axes[0]] 

ix = input.shape[axes[1]] 

if reshape: 

mtrx = numpy.array([[m11, -m21], 

[-m12, m22]], dtype=numpy.float64) 

minc = [0, 0] 

maxc = [0, 0] 

coor = numpy.dot(mtrx, [0, ix]) 

minc, maxc = _minmax(coor, minc, maxc) 

coor = numpy.dot(mtrx, [iy, 0]) 

minc, maxc = _minmax(coor, minc, maxc) 

coor = numpy.dot(mtrx, [iy, ix]) 

minc, maxc = _minmax(coor, minc, maxc) 

oy = int(maxc[0] - minc[0] + 0.5) 

ox = int(maxc[1] - minc[1] + 0.5) 

else: 

oy = input.shape[axes[0]] 

ox = input.shape[axes[1]] 

offset = numpy.zeros((2,), dtype=numpy.float64) 

offset[0] = float(oy) / 2.0 - 0.5 

offset[1] = float(ox) / 2.0 - 0.5 

offset = numpy.dot(matrix, offset) 

tmp = numpy.zeros((2,), dtype=numpy.float64) 

tmp[0] = float(iy) / 2.0 - 0.5 

tmp[1] = float(ix) / 2.0 - 0.5 

offset = tmp - offset 

output_shape = list(input.shape) 

output_shape[axes[0]] = oy 

output_shape[axes[1]] = ox 

output_shape = tuple(output_shape) 

output = _ni_support._get_output(output, input, 

shape=output_shape) 

if input.ndim <= 2: 

affine_transform(input, matrix, offset, output_shape, output, 

order, mode, cval, prefilter) 

else: 

coordinates = [] 

size = numpy.product(input.shape, axis=0) 

size //= input.shape[axes[0]] 

size //= input.shape[axes[1]] 

for ii in range(input.ndim): 

if ii not in axes: 

coordinates.append(0) 

else: 

coordinates.append(slice(None, None, None)) 

iter_axes = list(range(input.ndim)) 

iter_axes.reverse() 

iter_axes.remove(axes[0]) 

iter_axes.remove(axes[1]) 

os = (output_shape[axes[0]], output_shape[axes[1]]) 

for ii in range(size): 

ia = input[tuple(coordinates)] 

oa = output[tuple(coordinates)] 

affine_transform(ia, matrix, offset, os, oa, order, mode, 

cval, prefilter) 

for jj in iter_axes: 

if coordinates[jj] < input.shape[jj] - 1: 

coordinates[jj] += 1 

break 

else: 

coordinates[jj] = 0 

return output