Coverage for /usr/lib/python3/dist-packages/scipy/ndimage/_filters.py: 13%

494 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

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 

31from collections.abc import Iterable 

32import numbers 

33import warnings 

34import numpy 

35import operator 

36from numpy.core.multiarray import normalize_axis_index 

37from . import _ni_support 

38from . import _nd_image 

39from . import _ni_docstrings 

40 

41__all__ = ['correlate1d', 'convolve1d', 'gaussian_filter1d', 'gaussian_filter', 

42 'prewitt', 'sobel', 'generic_laplace', 'laplace', 

43 'gaussian_laplace', 'generic_gradient_magnitude', 

44 'gaussian_gradient_magnitude', 'correlate', 'convolve', 

45 'uniform_filter1d', 'uniform_filter', 'minimum_filter1d', 

46 'maximum_filter1d', 'minimum_filter', 'maximum_filter', 

47 'rank_filter', 'median_filter', 'percentile_filter', 

48 'generic_filter1d', 'generic_filter'] 

49 

50 

51def _invalid_origin(origin, lenw): 

52 return (origin < -(lenw // 2)) or (origin > (lenw - 1) // 2) 

53 

54 

55def _complex_via_real_components(func, input, weights, output, cval, **kwargs): 

56 """Complex convolution via a linear combination of real convolutions.""" 

57 complex_input = input.dtype.kind == 'c' 

58 complex_weights = weights.dtype.kind == 'c' 

59 if complex_input and complex_weights: 

60 # real component of the output 

61 func(input.real, weights.real, output=output.real, 

62 cval=numpy.real(cval), **kwargs) 

63 output.real -= func(input.imag, weights.imag, output=None, 

64 cval=numpy.imag(cval), **kwargs) 

65 # imaginary component of the output 

66 func(input.real, weights.imag, output=output.imag, 

67 cval=numpy.real(cval), **kwargs) 

68 output.imag += func(input.imag, weights.real, output=None, 

69 cval=numpy.imag(cval), **kwargs) 

70 elif complex_input: 

71 func(input.real, weights, output=output.real, cval=numpy.real(cval), 

72 **kwargs) 

73 func(input.imag, weights, output=output.imag, cval=numpy.imag(cval), 

74 **kwargs) 

75 else: 

76 if numpy.iscomplexobj(cval): 

77 raise ValueError("Cannot provide a complex-valued cval when the " 

78 "input is real.") 

79 func(input, weights.real, output=output.real, cval=cval, **kwargs) 

80 func(input, weights.imag, output=output.imag, cval=cval, **kwargs) 

81 return output 

82 

83 

84@_ni_docstrings.docfiller 

85def correlate1d(input, weights, axis=-1, output=None, mode="reflect", 

86 cval=0.0, origin=0): 

87 """Calculate a 1-D correlation along the given axis. 

88 

89 The lines of the array along the given axis are correlated with the 

90 given weights. 

91 

92 Parameters 

93 ---------- 

94 %(input)s 

95 weights : array 

96 1-D sequence of numbers. 

97 %(axis)s 

98 %(output)s 

99 %(mode_reflect)s 

100 %(cval)s 

101 %(origin)s 

102 

103 Examples 

104 -------- 

105 >>> from scipy.ndimage import correlate1d 

106 >>> correlate1d([2, 8, 0, 4, 1, 9, 9, 0], weights=[1, 3]) 

107 array([ 8, 26, 8, 12, 7, 28, 36, 9]) 

108 """ 

109 input = numpy.asarray(input) 

110 weights = numpy.asarray(weights) 

111 complex_input = input.dtype.kind == 'c' 

112 complex_weights = weights.dtype.kind == 'c' 

113 if complex_input or complex_weights: 

114 if complex_weights: 

115 weights = weights.conj() 

116 weights = weights.astype(numpy.complex128, copy=False) 

117 kwargs = dict(axis=axis, mode=mode, origin=origin) 

118 output = _ni_support._get_output(output, input, complex_output=True) 

119 return _complex_via_real_components(correlate1d, input, weights, 

120 output, cval, **kwargs) 

121 

122 output = _ni_support._get_output(output, input) 

123 weights = numpy.asarray(weights, dtype=numpy.float64) 

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

125 raise RuntimeError('no filter weights given') 

126 if not weights.flags.contiguous: 

127 weights = weights.copy() 

128 axis = normalize_axis_index(axis, input.ndim) 

129 if _invalid_origin(origin, len(weights)): 

130 raise ValueError('Invalid origin; origin must satisfy ' 

131 '-(len(weights) // 2) <= origin <= ' 

132 '(len(weights)-1) // 2') 

133 mode = _ni_support._extend_mode_to_code(mode) 

134 _nd_image.correlate1d(input, weights, axis, output, mode, cval, 

135 origin) 

136 return output 

137 

138 

139@_ni_docstrings.docfiller 

140def convolve1d(input, weights, axis=-1, output=None, mode="reflect", 

141 cval=0.0, origin=0): 

142 """Calculate a 1-D convolution along the given axis. 

143 

144 The lines of the array along the given axis are convolved with the 

145 given weights. 

146 

147 Parameters 

148 ---------- 

149 %(input)s 

150 weights : ndarray 

151 1-D sequence of numbers. 

152 %(axis)s 

153 %(output)s 

154 %(mode_reflect)s 

155 %(cval)s 

156 %(origin)s 

157 

158 Returns 

159 ------- 

160 convolve1d : ndarray 

161 Convolved array with same shape as input 

162 

163 Examples 

164 -------- 

165 >>> from scipy.ndimage import convolve1d 

166 >>> convolve1d([2, 8, 0, 4, 1, 9, 9, 0], weights=[1, 3]) 

167 array([14, 24, 4, 13, 12, 36, 27, 0]) 

168 """ 

169 weights = weights[::-1] 

170 origin = -origin 

171 if not len(weights) & 1: 

172 origin -= 1 

173 weights = numpy.asarray(weights) 

174 if weights.dtype.kind == 'c': 

175 # pre-conjugate here to counteract the conjugation in correlate1d 

176 weights = weights.conj() 

177 return correlate1d(input, weights, axis, output, mode, cval, origin) 

178 

179 

180def _gaussian_kernel1d(sigma, order, radius): 

181 """ 

182 Computes a 1-D Gaussian convolution kernel. 

183 """ 

184 if order < 0: 

185 raise ValueError('order must be non-negative') 

186 exponent_range = numpy.arange(order + 1) 

187 sigma2 = sigma * sigma 

188 x = numpy.arange(-radius, radius+1) 

189 phi_x = numpy.exp(-0.5 / sigma2 * x ** 2) 

190 phi_x = phi_x / phi_x.sum() 

191 

192 if order == 0: 

193 return phi_x 

194 else: 

195 # f(x) = q(x) * phi(x) = q(x) * exp(p(x)) 

196 # f'(x) = (q'(x) + q(x) * p'(x)) * phi(x) 

197 # p'(x) = -1 / sigma ** 2 

198 # Implement q'(x) + q(x) * p'(x) as a matrix operator and apply to the 

199 # coefficients of q(x) 

200 q = numpy.zeros(order + 1) 

201 q[0] = 1 

202 D = numpy.diag(exponent_range[1:], 1) # D @ q(x) = q'(x) 

203 P = numpy.diag(numpy.ones(order)/-sigma2, -1) # P @ q(x) = q(x) * p'(x) 

204 Q_deriv = D + P 

205 for _ in range(order): 

206 q = Q_deriv.dot(q) 

207 q = (x[:, None] ** exponent_range).dot(q) 

208 return q * phi_x 

209 

210 

211@_ni_docstrings.docfiller 

212def gaussian_filter1d(input, sigma, axis=-1, order=0, output=None, 

213 mode="reflect", cval=0.0, truncate=4.0, *, radius=None): 

214 """1-D Gaussian filter. 

215 

216 Parameters 

217 ---------- 

218 %(input)s 

219 sigma : scalar 

220 standard deviation for Gaussian kernel 

221 %(axis)s 

222 order : int, optional 

223 An order of 0 corresponds to convolution with a Gaussian 

224 kernel. A positive order corresponds to convolution with 

225 that derivative of a Gaussian. 

226 %(output)s 

227 %(mode_reflect)s 

228 %(cval)s 

229 truncate : float, optional 

230 Truncate the filter at this many standard deviations. 

231 Default is 4.0. 

232 radius : None or int, optional 

233 Radius of the Gaussian kernel. If specified, the size of 

234 the kernel will be ``2*radius + 1``, and `truncate` is ignored. 

235 Default is None. 

236 

237 Returns 

238 ------- 

239 gaussian_filter1d : ndarray 

240 

241 Notes 

242 ----- 

243 The Gaussian kernel will have size ``2*radius + 1`` along each axis. If 

244 `radius` is None, a default ``radius = round(truncate * sigma)`` will be 

245 used. 

246 

247 Examples 

248 -------- 

249 >>> from scipy.ndimage import gaussian_filter1d 

250 >>> import numpy as np 

251 >>> gaussian_filter1d([1.0, 2.0, 3.0, 4.0, 5.0], 1) 

252 array([ 1.42704095, 2.06782203, 3. , 3.93217797, 4.57295905]) 

253 >>> gaussian_filter1d([1.0, 2.0, 3.0, 4.0, 5.0], 4) 

254 array([ 2.91948343, 2.95023502, 3. , 3.04976498, 3.08051657]) 

255 >>> import matplotlib.pyplot as plt 

256 >>> rng = np.random.default_rng() 

257 >>> x = rng.standard_normal(101).cumsum() 

258 >>> y3 = gaussian_filter1d(x, 3) 

259 >>> y6 = gaussian_filter1d(x, 6) 

260 >>> plt.plot(x, 'k', label='original data') 

261 >>> plt.plot(y3, '--', label='filtered, sigma=3') 

262 >>> plt.plot(y6, ':', label='filtered, sigma=6') 

263 >>> plt.legend() 

264 >>> plt.grid() 

265 >>> plt.show() 

266 

267 """ 

268 sd = float(sigma) 

269 # make the radius of the filter equal to truncate standard deviations 

270 lw = int(truncate * sd + 0.5) 

271 if radius is not None: 

272 lw = radius 

273 if not isinstance(lw, numbers.Integral) or lw < 0: 

274 raise ValueError('Radius must be a nonnegative integer.') 

275 # Since we are calling correlate, not convolve, revert the kernel 

276 weights = _gaussian_kernel1d(sigma, order, lw)[::-1] 

277 return correlate1d(input, weights, axis, output, mode, cval, 0) 

278 

279 

280@_ni_docstrings.docfiller 

281def gaussian_filter(input, sigma, order=0, output=None, 

282 mode="reflect", cval=0.0, truncate=4.0, *, radius=None, 

283 axes=None): 

284 """Multidimensional Gaussian filter. 

285 

286 Parameters 

287 ---------- 

288 %(input)s 

289 sigma : scalar or sequence of scalars 

290 Standard deviation for Gaussian kernel. The standard 

291 deviations of the Gaussian filter are given for each axis as a 

292 sequence, or as a single number, in which case it is equal for 

293 all axes. 

294 order : int or sequence of ints, optional 

295 The order of the filter along each axis is given as a sequence 

296 of integers, or as a single number. An order of 0 corresponds 

297 to convolution with a Gaussian kernel. A positive order 

298 corresponds to convolution with that derivative of a Gaussian. 

299 %(output)s 

300 %(mode_multiple)s 

301 %(cval)s 

302 truncate : float, optional 

303 Truncate the filter at this many standard deviations. 

304 Default is 4.0. 

305 radius : None or int or sequence of ints, optional 

306 Radius of the Gaussian kernel. The radius are given for each axis 

307 as a sequence, or as a single number, in which case it is equal 

308 for all axes. If specified, the size of the kernel along each axis 

309 will be ``2*radius + 1``, and `truncate` is ignored. 

310 Default is None. 

311 axes : tuple of int or None, optional 

312 If None, `input` is filtered along all axes. Otherwise, 

313 `input` is filtered along the specified axes. When `axes` is 

314 specified, any tuples used for `sigma`, `order`, `mode` and/or `radius` 

315 must match the length of `axes`. The ith entry in any of these tuples 

316 corresponds to the ith entry in `axes`. 

317 

318 Returns 

319 ------- 

320 gaussian_filter : ndarray 

321 Returned array of same shape as `input`. 

322 

323 Notes 

324 ----- 

325 The multidimensional filter is implemented as a sequence of 

326 1-D convolution filters. The intermediate arrays are 

327 stored in the same data type as the output. Therefore, for output 

328 types with a limited precision, the results may be imprecise 

329 because intermediate results may be stored with insufficient 

330 precision. 

331 

332 The Gaussian kernel will have size ``2*radius + 1`` along each axis. If 

333 `radius` is None, the default ``radius = round(truncate * sigma)`` will be 

334 used. 

335 

336 Examples 

337 -------- 

338 >>> from scipy.ndimage import gaussian_filter 

339 >>> import numpy as np 

340 >>> a = np.arange(50, step=2).reshape((5,5)) 

341 >>> a 

342 array([[ 0, 2, 4, 6, 8], 

343 [10, 12, 14, 16, 18], 

344 [20, 22, 24, 26, 28], 

345 [30, 32, 34, 36, 38], 

346 [40, 42, 44, 46, 48]]) 

347 >>> gaussian_filter(a, sigma=1) 

348 array([[ 4, 6, 8, 9, 11], 

349 [10, 12, 14, 15, 17], 

350 [20, 22, 24, 25, 27], 

351 [29, 31, 33, 34, 36], 

352 [35, 37, 39, 40, 42]]) 

353 

354 >>> from scipy import datasets 

355 >>> import matplotlib.pyplot as plt 

356 >>> fig = plt.figure() 

357 >>> plt.gray() # show the filtered result in grayscale 

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

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

360 >>> ascent = datasets.ascent() 

361 >>> result = gaussian_filter(ascent, sigma=5) 

362 >>> ax1.imshow(ascent) 

363 >>> ax2.imshow(result) 

364 >>> plt.show() 

365 """ 

366 input = numpy.asarray(input) 

367 output = _ni_support._get_output(output, input) 

368 

369 axes = _ni_support._check_axes(axes, input.ndim) 

370 num_axes = len(axes) 

371 orders = _ni_support._normalize_sequence(order, num_axes) 

372 sigmas = _ni_support._normalize_sequence(sigma, num_axes) 

373 modes = _ni_support._normalize_sequence(mode, num_axes) 

374 radiuses = _ni_support._normalize_sequence(radius, num_axes) 

375 axes = [(axes[ii], sigmas[ii], orders[ii], modes[ii], radiuses[ii]) 

376 for ii in range(num_axes) if sigmas[ii] > 1e-15] 

377 if len(axes) > 0: 

378 for axis, sigma, order, mode, radius in axes: 

379 gaussian_filter1d(input, sigma, axis, order, output, 

380 mode, cval, truncate, radius=radius) 

381 input = output 

382 else: 

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

384 return output 

385 

386 

387@_ni_docstrings.docfiller 

388def prewitt(input, axis=-1, output=None, mode="reflect", cval=0.0): 

389 """Calculate a Prewitt filter. 

390 

391 Parameters 

392 ---------- 

393 %(input)s 

394 %(axis)s 

395 %(output)s 

396 %(mode_multiple)s 

397 %(cval)s 

398 

399 Examples 

400 -------- 

401 >>> from scipy import ndimage, datasets 

402 >>> import matplotlib.pyplot as plt 

403 >>> fig = plt.figure() 

404 >>> plt.gray() # show the filtered result in grayscale 

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

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

407 >>> ascent = datasets.ascent() 

408 >>> result = ndimage.prewitt(ascent) 

409 >>> ax1.imshow(ascent) 

410 >>> ax2.imshow(result) 

411 >>> plt.show() 

412 """ 

413 input = numpy.asarray(input) 

414 axis = normalize_axis_index(axis, input.ndim) 

415 output = _ni_support._get_output(output, input) 

416 modes = _ni_support._normalize_sequence(mode, input.ndim) 

417 correlate1d(input, [-1, 0, 1], axis, output, modes[axis], cval, 0) 

418 axes = [ii for ii in range(input.ndim) if ii != axis] 

419 for ii in axes: 

420 correlate1d(output, [1, 1, 1], ii, output, modes[ii], cval, 0,) 

421 return output 

422 

423 

424@_ni_docstrings.docfiller 

425def sobel(input, axis=-1, output=None, mode="reflect", cval=0.0): 

426 """Calculate a Sobel filter. 

427 

428 Parameters 

429 ---------- 

430 %(input)s 

431 %(axis)s 

432 %(output)s 

433 %(mode_multiple)s 

434 %(cval)s 

435 

436 Notes 

437 ----- 

438 This function computes the axis-specific Sobel gradient. 

439 The horizontal edges can emphasised with the horizontal trasform (axis=0), 

440 the vertical edges with the vertical transform (axis=1) and so on for higher 

441 dimensions. These can be combined to give the magnitude. 

442 

443 Examples 

444 -------- 

445 >>> from scipy import ndimage, datasets 

446 >>> import matplotlib.pyplot as plt 

447 >>> import numpy as np 

448 >>> ascent = datasets.ascent().astype('int32') 

449 >>> sobel_h = ndimage.sobel(ascent, 0) # horizontal gradient 

450 >>> sobel_v = ndimage.sobel(ascent, 1) # vertical gradient 

451 >>> magnitude = np.sqrt(sobel_h**2 + sobel_v**2) 

452 >>> magnitude *= 255.0 / np.max(magnitude) # normalization 

453 >>> fig, axs = plt.subplots(2, 2, figsize=(8, 8)) 

454 >>> plt.gray() # show the filtered result in grayscale 

455 >>> axs[0, 0].imshow(ascent) 

456 >>> axs[0, 1].imshow(sobel_h) 

457 >>> axs[1, 0].imshow(sobel_v) 

458 >>> axs[1, 1].imshow(magnitude) 

459 >>> titles = ["original", "horizontal", "vertical", "magnitude"] 

460 >>> for i, ax in enumerate(axs.ravel()): 

461 ... ax.set_title(titles[i]) 

462 ... ax.axis("off") 

463 >>> plt.show() 

464 

465 """ 

466 input = numpy.asarray(input) 

467 axis = normalize_axis_index(axis, input.ndim) 

468 output = _ni_support._get_output(output, input) 

469 modes = _ni_support._normalize_sequence(mode, input.ndim) 

470 correlate1d(input, [-1, 0, 1], axis, output, modes[axis], cval, 0) 

471 axes = [ii for ii in range(input.ndim) if ii != axis] 

472 for ii in axes: 

473 correlate1d(output, [1, 2, 1], ii, output, modes[ii], cval, 0) 

474 return output 

475 

476 

477@_ni_docstrings.docfiller 

478def generic_laplace(input, derivative2, output=None, mode="reflect", 

479 cval=0.0, 

480 extra_arguments=(), 

481 extra_keywords=None): 

482 """ 

483 N-D Laplace filter using a provided second derivative function. 

484 

485 Parameters 

486 ---------- 

487 %(input)s 

488 derivative2 : callable 

489 Callable with the following signature:: 

490 

491 derivative2(input, axis, output, mode, cval, 

492 *extra_arguments, **extra_keywords) 

493 

494 See `extra_arguments`, `extra_keywords` below. 

495 %(output)s 

496 %(mode_multiple)s 

497 %(cval)s 

498 %(extra_keywords)s 

499 %(extra_arguments)s 

500 """ 

501 if extra_keywords is None: 

502 extra_keywords = {} 

503 input = numpy.asarray(input) 

504 output = _ni_support._get_output(output, input) 

505 axes = list(range(input.ndim)) 

506 if len(axes) > 0: 

507 modes = _ni_support._normalize_sequence(mode, len(axes)) 

508 derivative2(input, axes[0], output, modes[0], cval, 

509 *extra_arguments, **extra_keywords) 

510 for ii in range(1, len(axes)): 

511 tmp = derivative2(input, axes[ii], output.dtype, modes[ii], cval, 

512 *extra_arguments, **extra_keywords) 

513 output += tmp 

514 else: 

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

516 return output 

517 

518 

519@_ni_docstrings.docfiller 

520def laplace(input, output=None, mode="reflect", cval=0.0): 

521 """N-D Laplace filter based on approximate second derivatives. 

522 

523 Parameters 

524 ---------- 

525 %(input)s 

526 %(output)s 

527 %(mode_multiple)s 

528 %(cval)s 

529 

530 Examples 

531 -------- 

532 >>> from scipy import ndimage, datasets 

533 >>> import matplotlib.pyplot as plt 

534 >>> fig = plt.figure() 

535 >>> plt.gray() # show the filtered result in grayscale 

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

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

538 >>> ascent = datasets.ascent() 

539 >>> result = ndimage.laplace(ascent) 

540 >>> ax1.imshow(ascent) 

541 >>> ax2.imshow(result) 

542 >>> plt.show() 

543 """ 

544 def derivative2(input, axis, output, mode, cval): 

545 return correlate1d(input, [1, -2, 1], axis, output, mode, cval, 0) 

546 return generic_laplace(input, derivative2, output, mode, cval) 

547 

548 

549@_ni_docstrings.docfiller 

550def gaussian_laplace(input, sigma, output=None, mode="reflect", 

551 cval=0.0, **kwargs): 

552 """Multidimensional Laplace filter using Gaussian second derivatives. 

553 

554 Parameters 

555 ---------- 

556 %(input)s 

557 sigma : scalar or sequence of scalars 

558 The standard deviations of the Gaussian filter are given for 

559 each axis as a sequence, or as a single number, in which case 

560 it is equal for all axes. 

561 %(output)s 

562 %(mode_multiple)s 

563 %(cval)s 

564 Extra keyword arguments will be passed to gaussian_filter(). 

565 

566 Examples 

567 -------- 

568 >>> from scipy import ndimage, datasets 

569 >>> import matplotlib.pyplot as plt 

570 >>> ascent = datasets.ascent() 

571 

572 >>> fig = plt.figure() 

573 >>> plt.gray() # show the filtered result in grayscale 

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

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

576 

577 >>> result = ndimage.gaussian_laplace(ascent, sigma=1) 

578 >>> ax1.imshow(result) 

579 

580 >>> result = ndimage.gaussian_laplace(ascent, sigma=3) 

581 >>> ax2.imshow(result) 

582 >>> plt.show() 

583 """ 

584 input = numpy.asarray(input) 

585 

586 def derivative2(input, axis, output, mode, cval, sigma, **kwargs): 

587 order = [0] * input.ndim 

588 order[axis] = 2 

589 return gaussian_filter(input, sigma, order, output, mode, cval, 

590 **kwargs) 

591 

592 return generic_laplace(input, derivative2, output, mode, cval, 

593 extra_arguments=(sigma,), 

594 extra_keywords=kwargs) 

595 

596 

597@_ni_docstrings.docfiller 

598def generic_gradient_magnitude(input, derivative, output=None, 

599 mode="reflect", cval=0.0, 

600 extra_arguments=(), extra_keywords=None): 

601 """Gradient magnitude using a provided gradient function. 

602 

603 Parameters 

604 ---------- 

605 %(input)s 

606 derivative : callable 

607 Callable with the following signature:: 

608 

609 derivative(input, axis, output, mode, cval, 

610 *extra_arguments, **extra_keywords) 

611 

612 See `extra_arguments`, `extra_keywords` below. 

613 `derivative` can assume that `input` and `output` are ndarrays. 

614 Note that the output from `derivative` is modified inplace; 

615 be careful to copy important inputs before returning them. 

616 %(output)s 

617 %(mode_multiple)s 

618 %(cval)s 

619 %(extra_keywords)s 

620 %(extra_arguments)s 

621 """ 

622 if extra_keywords is None: 

623 extra_keywords = {} 

624 input = numpy.asarray(input) 

625 output = _ni_support._get_output(output, input) 

626 axes = list(range(input.ndim)) 

627 if len(axes) > 0: 

628 modes = _ni_support._normalize_sequence(mode, len(axes)) 

629 derivative(input, axes[0], output, modes[0], cval, 

630 *extra_arguments, **extra_keywords) 

631 numpy.multiply(output, output, output) 

632 for ii in range(1, len(axes)): 

633 tmp = derivative(input, axes[ii], output.dtype, modes[ii], cval, 

634 *extra_arguments, **extra_keywords) 

635 numpy.multiply(tmp, tmp, tmp) 

636 output += tmp 

637 # This allows the sqrt to work with a different default casting 

638 numpy.sqrt(output, output, casting='unsafe') 

639 else: 

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

641 return output 

642 

643 

644@_ni_docstrings.docfiller 

645def gaussian_gradient_magnitude(input, sigma, output=None, 

646 mode="reflect", cval=0.0, **kwargs): 

647 """Multidimensional gradient magnitude using Gaussian derivatives. 

648 

649 Parameters 

650 ---------- 

651 %(input)s 

652 sigma : scalar or sequence of scalars 

653 The standard deviations of the Gaussian filter are given for 

654 each axis as a sequence, or as a single number, in which case 

655 it is equal for all axes. 

656 %(output)s 

657 %(mode_multiple)s 

658 %(cval)s 

659 Extra keyword arguments will be passed to gaussian_filter(). 

660 

661 Returns 

662 ------- 

663 gaussian_gradient_magnitude : ndarray 

664 Filtered array. Has the same shape as `input`. 

665 

666 Examples 

667 -------- 

668 >>> from scipy import ndimage, datasets 

669 >>> import matplotlib.pyplot as plt 

670 >>> fig = plt.figure() 

671 >>> plt.gray() # show the filtered result in grayscale 

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

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

674 >>> ascent = datasets.ascent() 

675 >>> result = ndimage.gaussian_gradient_magnitude(ascent, sigma=5) 

676 >>> ax1.imshow(ascent) 

677 >>> ax2.imshow(result) 

678 >>> plt.show() 

679 """ 

680 input = numpy.asarray(input) 

681 

682 def derivative(input, axis, output, mode, cval, sigma, **kwargs): 

683 order = [0] * input.ndim 

684 order[axis] = 1 

685 return gaussian_filter(input, sigma, order, output, mode, 

686 cval, **kwargs) 

687 

688 return generic_gradient_magnitude(input, derivative, output, mode, 

689 cval, extra_arguments=(sigma,), 

690 extra_keywords=kwargs) 

691 

692 

693def _correlate_or_convolve(input, weights, output, mode, cval, origin, 

694 convolution): 

695 input = numpy.asarray(input) 

696 weights = numpy.asarray(weights) 

697 complex_input = input.dtype.kind == 'c' 

698 complex_weights = weights.dtype.kind == 'c' 

699 if complex_input or complex_weights: 

700 if complex_weights and not convolution: 

701 # As for numpy.correlate, conjugate weights rather than input. 

702 weights = weights.conj() 

703 kwargs = dict( 

704 mode=mode, origin=origin, convolution=convolution 

705 ) 

706 output = _ni_support._get_output(output, input, complex_output=True) 

707 

708 return _complex_via_real_components(_correlate_or_convolve, input, 

709 weights, output, cval, **kwargs) 

710 

711 origins = _ni_support._normalize_sequence(origin, input.ndim) 

712 weights = numpy.asarray(weights, dtype=numpy.float64) 

713 wshape = [ii for ii in weights.shape if ii > 0] 

714 if len(wshape) != input.ndim: 

715 raise RuntimeError('filter weights array has incorrect shape.') 

716 if convolution: 

717 weights = weights[tuple([slice(None, None, -1)] * weights.ndim)] 

718 for ii in range(len(origins)): 

719 origins[ii] = -origins[ii] 

720 if not weights.shape[ii] & 1: 

721 origins[ii] -= 1 

722 for origin, lenw in zip(origins, wshape): 

723 if _invalid_origin(origin, lenw): 

724 raise ValueError('Invalid origin; origin must satisfy ' 

725 '-(weights.shape[k] // 2) <= origin[k] <= ' 

726 '(weights.shape[k]-1) // 2') 

727 

728 if not weights.flags.contiguous: 

729 weights = weights.copy() 

730 output = _ni_support._get_output(output, input) 

731 temp_needed = numpy.may_share_memory(input, output) 

732 if temp_needed: 

733 # input and output arrays cannot share memory 

734 temp = output 

735 output = _ni_support._get_output(output.dtype, input) 

736 if not isinstance(mode, str) and isinstance(mode, Iterable): 

737 raise RuntimeError("A sequence of modes is not supported") 

738 mode = _ni_support._extend_mode_to_code(mode) 

739 _nd_image.correlate(input, weights, output, mode, cval, origins) 

740 if temp_needed: 

741 temp[...] = output 

742 output = temp 

743 return output 

744 

745 

746@_ni_docstrings.docfiller 

747def correlate(input, weights, output=None, mode='reflect', cval=0.0, 

748 origin=0): 

749 """ 

750 Multidimensional correlation. 

751 

752 The array is correlated with the given kernel. 

753 

754 Parameters 

755 ---------- 

756 %(input)s 

757 weights : ndarray 

758 array of weights, same number of dimensions as input 

759 %(output)s 

760 %(mode_reflect)s 

761 %(cval)s 

762 %(origin_multiple)s 

763 

764 Returns 

765 ------- 

766 result : ndarray 

767 The result of correlation of `input` with `weights`. 

768 

769 See Also 

770 -------- 

771 convolve : Convolve an image with a kernel. 

772 

773 Examples 

774 -------- 

775 Correlation is the process of moving a filter mask often referred to 

776 as kernel over the image and computing the sum of products at each location. 

777 

778 >>> from scipy.ndimage import correlate 

779 >>> import numpy as np 

780 >>> input_img = np.arange(25).reshape(5,5) 

781 >>> print(input_img) 

782 [[ 0 1 2 3 4] 

783 [ 5 6 7 8 9] 

784 [10 11 12 13 14] 

785 [15 16 17 18 19] 

786 [20 21 22 23 24]] 

787 

788 Define a kernel (weights) for correlation. In this example, it is for sum of 

789 center and up, down, left and right next elements. 

790 

791 >>> weights = [[0, 1, 0], 

792 ... [1, 1, 1], 

793 ... [0, 1, 0]] 

794 

795 We can calculate a correlation result: 

796 For example, element ``[2,2]`` is ``7 + 11 + 12 + 13 + 17 = 60``. 

797 

798 >>> correlate(input_img, weights) 

799 array([[ 6, 10, 15, 20, 24], 

800 [ 26, 30, 35, 40, 44], 

801 [ 51, 55, 60, 65, 69], 

802 [ 76, 80, 85, 90, 94], 

803 [ 96, 100, 105, 110, 114]]) 

804 

805 """ 

806 return _correlate_or_convolve(input, weights, output, mode, cval, 

807 origin, False) 

808 

809 

810@_ni_docstrings.docfiller 

811def convolve(input, weights, output=None, mode='reflect', cval=0.0, 

812 origin=0): 

813 """ 

814 Multidimensional convolution. 

815 

816 The array is convolved with the given kernel. 

817 

818 Parameters 

819 ---------- 

820 %(input)s 

821 weights : array_like 

822 Array of weights, same number of dimensions as input 

823 %(output)s 

824 %(mode_reflect)s 

825 cval : scalar, optional 

826 Value to fill past edges of input if `mode` is 'constant'. Default 

827 is 0.0 

828 origin : int, optional 

829 Controls the origin of the input signal, which is where the 

830 filter is centered to produce the first element of the output. 

831 Positive values shift the filter to the right, and negative values 

832 shift the filter to the left. Default is 0. 

833 

834 Returns 

835 ------- 

836 result : ndarray 

837 The result of convolution of `input` with `weights`. 

838 

839 See Also 

840 -------- 

841 correlate : Correlate an image with a kernel. 

842 

843 Notes 

844 ----- 

845 Each value in result is :math:`C_i = \\sum_j{I_{i+k-j} W_j}`, where 

846 W is the `weights` kernel, 

847 j is the N-D spatial index over :math:`W`, 

848 I is the `input` and k is the coordinate of the center of 

849 W, specified by `origin` in the input parameters. 

850 

851 Examples 

852 -------- 

853 Perhaps the simplest case to understand is ``mode='constant', cval=0.0``, 

854 because in this case borders (i.e., where the `weights` kernel, centered 

855 on any one value, extends beyond an edge of `input`) are treated as zeros. 

856 

857 >>> import numpy as np 

858 >>> a = np.array([[1, 2, 0, 0], 

859 ... [5, 3, 0, 4], 

860 ... [0, 0, 0, 7], 

861 ... [9, 3, 0, 0]]) 

862 >>> k = np.array([[1,1,1],[1,1,0],[1,0,0]]) 

863 >>> from scipy import ndimage 

864 >>> ndimage.convolve(a, k, mode='constant', cval=0.0) 

865 array([[11, 10, 7, 4], 

866 [10, 3, 11, 11], 

867 [15, 12, 14, 7], 

868 [12, 3, 7, 0]]) 

869 

870 Setting ``cval=1.0`` is equivalent to padding the outer edge of `input` 

871 with 1.0's (and then extracting only the original region of the result). 

872 

873 >>> ndimage.convolve(a, k, mode='constant', cval=1.0) 

874 array([[13, 11, 8, 7], 

875 [11, 3, 11, 14], 

876 [16, 12, 14, 10], 

877 [15, 6, 10, 5]]) 

878 

879 With ``mode='reflect'`` (the default), outer values are reflected at the 

880 edge of `input` to fill in missing values. 

881 

882 >>> b = np.array([[2, 0, 0], 

883 ... [1, 0, 0], 

884 ... [0, 0, 0]]) 

885 >>> k = np.array([[0,1,0], [0,1,0], [0,1,0]]) 

886 >>> ndimage.convolve(b, k, mode='reflect') 

887 array([[5, 0, 0], 

888 [3, 0, 0], 

889 [1, 0, 0]]) 

890 

891 This includes diagonally at the corners. 

892 

893 >>> k = np.array([[1,0,0],[0,1,0],[0,0,1]]) 

894 >>> ndimage.convolve(b, k) 

895 array([[4, 2, 0], 

896 [3, 2, 0], 

897 [1, 1, 0]]) 

898 

899 With ``mode='nearest'``, the single nearest value in to an edge in 

900 `input` is repeated as many times as needed to match the overlapping 

901 `weights`. 

902 

903 >>> c = np.array([[2, 0, 1], 

904 ... [1, 0, 0], 

905 ... [0, 0, 0]]) 

906 >>> k = np.array([[0, 1, 0], 

907 ... [0, 1, 0], 

908 ... [0, 1, 0], 

909 ... [0, 1, 0], 

910 ... [0, 1, 0]]) 

911 >>> ndimage.convolve(c, k, mode='nearest') 

912 array([[7, 0, 3], 

913 [5, 0, 2], 

914 [3, 0, 1]]) 

915 

916 """ 

917 return _correlate_or_convolve(input, weights, output, mode, cval, 

918 origin, True) 

919 

920 

921@_ni_docstrings.docfiller 

922def uniform_filter1d(input, size, axis=-1, output=None, 

923 mode="reflect", cval=0.0, origin=0): 

924 """Calculate a 1-D uniform filter along the given axis. 

925 

926 The lines of the array along the given axis are filtered with a 

927 uniform filter of given size. 

928 

929 Parameters 

930 ---------- 

931 %(input)s 

932 size : int 

933 length of uniform filter 

934 %(axis)s 

935 %(output)s 

936 %(mode_reflect)s 

937 %(cval)s 

938 %(origin)s 

939 

940 Examples 

941 -------- 

942 >>> from scipy.ndimage import uniform_filter1d 

943 >>> uniform_filter1d([2, 8, 0, 4, 1, 9, 9, 0], size=3) 

944 array([4, 3, 4, 1, 4, 6, 6, 3]) 

945 """ 

946 input = numpy.asarray(input) 

947 axis = normalize_axis_index(axis, input.ndim) 

948 if size < 1: 

949 raise RuntimeError('incorrect filter size') 

950 complex_output = input.dtype.kind == 'c' 

951 output = _ni_support._get_output(output, input, 

952 complex_output=complex_output) 

953 if (size // 2 + origin < 0) or (size // 2 + origin >= size): 

954 raise ValueError('invalid origin') 

955 mode = _ni_support._extend_mode_to_code(mode) 

956 if not complex_output: 

957 _nd_image.uniform_filter1d(input, size, axis, output, mode, cval, 

958 origin) 

959 else: 

960 _nd_image.uniform_filter1d(input.real, size, axis, output.real, mode, 

961 numpy.real(cval), origin) 

962 _nd_image.uniform_filter1d(input.imag, size, axis, output.imag, mode, 

963 numpy.imag(cval), origin) 

964 return output 

965 

966 

967@_ni_docstrings.docfiller 

968def uniform_filter(input, size=3, output=None, mode="reflect", 

969 cval=0.0, origin=0, *, axes=None): 

970 """Multidimensional uniform filter. 

971 

972 Parameters 

973 ---------- 

974 %(input)s 

975 size : int or sequence of ints, optional 

976 The sizes of the uniform filter are given for each axis as a 

977 sequence, or as a single number, in which case the size is 

978 equal for all axes. 

979 %(output)s 

980 %(mode_multiple)s 

981 %(cval)s 

982 %(origin_multiple)s 

983 axes : tuple of int or None, optional 

984 If None, `input` is filtered along all axes. Otherwise, 

985 `input` is filtered along the specified axes. When `axes` is 

986 specified, any tuples used for `size`, `origin`, and/or `mode` 

987 must match the length of `axes`. The ith entry in any of these tuples 

988 corresponds to the ith entry in `axes`. 

989 

990 Returns 

991 ------- 

992 uniform_filter : ndarray 

993 Filtered array. Has the same shape as `input`. 

994 

995 Notes 

996 ----- 

997 The multidimensional filter is implemented as a sequence of 

998 1-D uniform filters. The intermediate arrays are stored 

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

1000 with a limited precision, the results may be imprecise because 

1001 intermediate results may be stored with insufficient precision. 

1002 

1003 Examples 

1004 -------- 

1005 >>> from scipy import ndimage, datasets 

1006 >>> import matplotlib.pyplot as plt 

1007 >>> fig = plt.figure() 

1008 >>> plt.gray() # show the filtered result in grayscale 

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

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

1011 >>> ascent = datasets.ascent() 

1012 >>> result = ndimage.uniform_filter(ascent, size=20) 

1013 >>> ax1.imshow(ascent) 

1014 >>> ax2.imshow(result) 

1015 >>> plt.show() 

1016 """ 

1017 input = numpy.asarray(input) 

1018 output = _ni_support._get_output(output, input, 

1019 complex_output=input.dtype.kind == 'c') 

1020 axes = _ni_support._check_axes(axes, input.ndim) 

1021 num_axes = len(axes) 

1022 sizes = _ni_support._normalize_sequence(size, num_axes) 

1023 origins = _ni_support._normalize_sequence(origin, num_axes) 

1024 modes = _ni_support._normalize_sequence(mode, num_axes) 

1025 axes = [(axes[ii], sizes[ii], origins[ii], modes[ii]) 

1026 for ii in range(num_axes) if sizes[ii] > 1] 

1027 if len(axes) > 0: 

1028 for axis, size, origin, mode in axes: 

1029 uniform_filter1d(input, int(size), axis, output, mode, 

1030 cval, origin) 

1031 input = output 

1032 else: 

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

1034 return output 

1035 

1036 

1037@_ni_docstrings.docfiller 

1038def minimum_filter1d(input, size, axis=-1, output=None, 

1039 mode="reflect", cval=0.0, origin=0): 

1040 """Calculate a 1-D minimum filter along the given axis. 

1041 

1042 The lines of the array along the given axis are filtered with a 

1043 minimum filter of given size. 

1044 

1045 Parameters 

1046 ---------- 

1047 %(input)s 

1048 size : int 

1049 length along which to calculate 1D minimum 

1050 %(axis)s 

1051 %(output)s 

1052 %(mode_reflect)s 

1053 %(cval)s 

1054 %(origin)s 

1055 

1056 Notes 

1057 ----- 

1058 This function implements the MINLIST algorithm [1]_, as described by 

1059 Richard Harter [2]_, and has a guaranteed O(n) performance, `n` being 

1060 the `input` length, regardless of filter size. 

1061 

1062 References 

1063 ---------- 

1064 .. [1] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.42.2777 

1065 .. [2] http://www.richardhartersworld.com/cri/2001/slidingmin.html 

1066 

1067 

1068 Examples 

1069 -------- 

1070 >>> from scipy.ndimage import minimum_filter1d 

1071 >>> minimum_filter1d([2, 8, 0, 4, 1, 9, 9, 0], size=3) 

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

1073 """ 

1074 input = numpy.asarray(input) 

1075 if numpy.iscomplexobj(input): 

1076 raise TypeError('Complex type not supported') 

1077 axis = normalize_axis_index(axis, input.ndim) 

1078 if size < 1: 

1079 raise RuntimeError('incorrect filter size') 

1080 output = _ni_support._get_output(output, input) 

1081 if (size // 2 + origin < 0) or (size // 2 + origin >= size): 

1082 raise ValueError('invalid origin') 

1083 mode = _ni_support._extend_mode_to_code(mode) 

1084 _nd_image.min_or_max_filter1d(input, size, axis, output, mode, cval, 

1085 origin, 1) 

1086 return output 

1087 

1088 

1089@_ni_docstrings.docfiller 

1090def maximum_filter1d(input, size, axis=-1, output=None, 

1091 mode="reflect", cval=0.0, origin=0): 

1092 """Calculate a 1-D maximum filter along the given axis. 

1093 

1094 The lines of the array along the given axis are filtered with a 

1095 maximum filter of given size. 

1096 

1097 Parameters 

1098 ---------- 

1099 %(input)s 

1100 size : int 

1101 Length along which to calculate the 1-D maximum. 

1102 %(axis)s 

1103 %(output)s 

1104 %(mode_reflect)s 

1105 %(cval)s 

1106 %(origin)s 

1107 

1108 Returns 

1109 ------- 

1110 maximum1d : ndarray, None 

1111 Maximum-filtered array with same shape as input. 

1112 None if `output` is not None 

1113 

1114 Notes 

1115 ----- 

1116 This function implements the MAXLIST algorithm [1]_, as described by 

1117 Richard Harter [2]_, and has a guaranteed O(n) performance, `n` being 

1118 the `input` length, regardless of filter size. 

1119 

1120 References 

1121 ---------- 

1122 .. [1] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.42.2777 

1123 .. [2] http://www.richardhartersworld.com/cri/2001/slidingmin.html 

1124 

1125 Examples 

1126 -------- 

1127 >>> from scipy.ndimage import maximum_filter1d 

1128 >>> maximum_filter1d([2, 8, 0, 4, 1, 9, 9, 0], size=3) 

1129 array([8, 8, 8, 4, 9, 9, 9, 9]) 

1130 """ 

1131 input = numpy.asarray(input) 

1132 if numpy.iscomplexobj(input): 

1133 raise TypeError('Complex type not supported') 

1134 axis = normalize_axis_index(axis, input.ndim) 

1135 if size < 1: 

1136 raise RuntimeError('incorrect filter size') 

1137 output = _ni_support._get_output(output, input) 

1138 if (size // 2 + origin < 0) or (size // 2 + origin >= size): 

1139 raise ValueError('invalid origin') 

1140 mode = _ni_support._extend_mode_to_code(mode) 

1141 _nd_image.min_or_max_filter1d(input, size, axis, output, mode, cval, 

1142 origin, 0) 

1143 return output 

1144 

1145 

1146def _min_or_max_filter(input, size, footprint, structure, output, mode, 

1147 cval, origin, minimum, axes=None): 

1148 if (size is not None) and (footprint is not None): 

1149 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=3) 

1150 if structure is None: 

1151 if footprint is None: 

1152 if size is None: 

1153 raise RuntimeError("no footprint provided") 

1154 separable = True 

1155 else: 

1156 footprint = numpy.asarray(footprint, dtype=bool) 

1157 if not footprint.any(): 

1158 raise ValueError("All-zero footprint is not supported.") 

1159 if footprint.all(): 

1160 size = footprint.shape 

1161 footprint = None 

1162 separable = True 

1163 else: 

1164 separable = False 

1165 else: 

1166 structure = numpy.asarray(structure, dtype=numpy.float64) 

1167 separable = False 

1168 if footprint is None: 

1169 footprint = numpy.ones(structure.shape, bool) 

1170 else: 

1171 footprint = numpy.asarray(footprint, dtype=bool) 

1172 input = numpy.asarray(input) 

1173 if numpy.iscomplexobj(input): 

1174 raise TypeError('Complex type not supported') 

1175 output = _ni_support._get_output(output, input) 

1176 temp_needed = numpy.may_share_memory(input, output) 

1177 if temp_needed: 

1178 # input and output arrays cannot share memory 

1179 temp = output 

1180 output = _ni_support._get_output(output.dtype, input) 

1181 axes = _ni_support._check_axes(axes, input.ndim) 

1182 num_axes = len(axes) 

1183 if separable: 

1184 origins = _ni_support._normalize_sequence(origin, num_axes) 

1185 sizes = _ni_support._normalize_sequence(size, num_axes) 

1186 modes = _ni_support._normalize_sequence(mode, num_axes) 

1187 axes = [(axes[ii], sizes[ii], origins[ii], modes[ii]) 

1188 for ii in range(len(axes)) if sizes[ii] > 1] 

1189 if minimum: 

1190 filter_ = minimum_filter1d 

1191 else: 

1192 filter_ = maximum_filter1d 

1193 if len(axes) > 0: 

1194 for axis, size, origin, mode in axes: 

1195 filter_(input, int(size), axis, output, mode, cval, origin) 

1196 input = output 

1197 else: 

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

1199 else: 

1200 origins = _ni_support._normalize_sequence(origin, input.ndim) 

1201 if num_axes < input.ndim: 

1202 if footprint.ndim != num_axes: 

1203 raise RuntimeError("footprint array has incorrect shape") 

1204 footprint = numpy.expand_dims( 

1205 footprint, 

1206 tuple(ax for ax in range(input.ndim) if ax not in axes) 

1207 ) 

1208 fshape = [ii for ii in footprint.shape if ii > 0] 

1209 if len(fshape) != input.ndim: 

1210 raise RuntimeError('footprint array has incorrect shape.') 

1211 for origin, lenf in zip(origins, fshape): 

1212 if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf): 

1213 raise ValueError('invalid origin') 

1214 if not footprint.flags.contiguous: 

1215 footprint = footprint.copy() 

1216 if structure is not None: 

1217 if len(structure.shape) != input.ndim: 

1218 raise RuntimeError('structure array has incorrect shape') 

1219 if num_axes != structure.ndim: 

1220 structure = numpy.expand_dims( 

1221 structure, 

1222 tuple(ax for ax in range(structure.ndim) if ax not in axes) 

1223 ) 

1224 if not structure.flags.contiguous: 

1225 structure = structure.copy() 

1226 if not isinstance(mode, str) and isinstance(mode, Iterable): 

1227 raise RuntimeError( 

1228 "A sequence of modes is not supported for non-separable " 

1229 "footprints") 

1230 mode = _ni_support._extend_mode_to_code(mode) 

1231 _nd_image.min_or_max_filter(input, footprint, structure, output, 

1232 mode, cval, origins, minimum) 

1233 if temp_needed: 

1234 temp[...] = output 

1235 output = temp 

1236 return output 

1237 

1238 

1239@_ni_docstrings.docfiller 

1240def minimum_filter(input, size=None, footprint=None, output=None, 

1241 mode="reflect", cval=0.0, origin=0, *, axes=None): 

1242 """Calculate a multidimensional minimum filter. 

1243 

1244 Parameters 

1245 ---------- 

1246 %(input)s 

1247 %(size_foot)s 

1248 %(output)s 

1249 %(mode_multiple)s 

1250 %(cval)s 

1251 %(origin_multiple)s 

1252 axes : tuple of int or None, optional 

1253 If None, `input` is filtered along all axes. Otherwise, 

1254 `input` is filtered along the specified axes. When `axes` is 

1255 specified, any tuples used for `size`, `origin`, and/or `mode` 

1256 must match the length of `axes`. The ith entry in any of these tuples 

1257 corresponds to the ith entry in `axes`. 

1258 

1259 Returns 

1260 ------- 

1261 minimum_filter : ndarray 

1262 Filtered array. Has the same shape as `input`. 

1263 

1264 Notes 

1265 ----- 

1266 A sequence of modes (one per axis) is only supported when the footprint is 

1267 separable. Otherwise, a single mode string must be provided. 

1268 

1269 Examples 

1270 -------- 

1271 >>> from scipy import ndimage, datasets 

1272 >>> import matplotlib.pyplot as plt 

1273 >>> fig = plt.figure() 

1274 >>> plt.gray() # show the filtered result in grayscale 

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

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

1277 >>> ascent = datasets.ascent() 

1278 >>> result = ndimage.minimum_filter(ascent, size=20) 

1279 >>> ax1.imshow(ascent) 

1280 >>> ax2.imshow(result) 

1281 >>> plt.show() 

1282 """ 

1283 return _min_or_max_filter(input, size, footprint, None, output, mode, 

1284 cval, origin, 1, axes) 

1285 

1286 

1287@_ni_docstrings.docfiller 

1288def maximum_filter(input, size=None, footprint=None, output=None, 

1289 mode="reflect", cval=0.0, origin=0, *, axes=None): 

1290 """Calculate a multidimensional maximum filter. 

1291 

1292 Parameters 

1293 ---------- 

1294 %(input)s 

1295 %(size_foot)s 

1296 %(output)s 

1297 %(mode_multiple)s 

1298 %(cval)s 

1299 %(origin_multiple)s 

1300 axes : tuple of int or None, optional 

1301 If None, `input` is filtered along all axes. Otherwise, 

1302 `input` is filtered along the specified axes. When `axes` is 

1303 specified, any tuples used for `size`, `origin`, and/or `mode` 

1304 must match the length of `axes`. The ith entry in any of these tuples 

1305 corresponds to the ith entry in `axes`. 

1306 

1307 Returns 

1308 ------- 

1309 maximum_filter : ndarray 

1310 Filtered array. Has the same shape as `input`. 

1311 

1312 Notes 

1313 ----- 

1314 A sequence of modes (one per axis) is only supported when the footprint is 

1315 separable. Otherwise, a single mode string must be provided. 

1316 

1317 Examples 

1318 -------- 

1319 >>> from scipy import ndimage, datasets 

1320 >>> import matplotlib.pyplot as plt 

1321 >>> fig = plt.figure() 

1322 >>> plt.gray() # show the filtered result in grayscale 

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

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

1325 >>> ascent = datasets.ascent() 

1326 >>> result = ndimage.maximum_filter(ascent, size=20) 

1327 >>> ax1.imshow(ascent) 

1328 >>> ax2.imshow(result) 

1329 >>> plt.show() 

1330 """ 

1331 return _min_or_max_filter(input, size, footprint, None, output, mode, 

1332 cval, origin, 0, axes) 

1333 

1334 

1335@_ni_docstrings.docfiller 

1336def _rank_filter(input, rank, size=None, footprint=None, output=None, 

1337 mode="reflect", cval=0.0, origin=0, operation='rank', 

1338 axes=None): 

1339 if (size is not None) and (footprint is not None): 

1340 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=3) 

1341 input = numpy.asarray(input) 

1342 if numpy.iscomplexobj(input): 

1343 raise TypeError('Complex type not supported') 

1344 axes = _ni_support._check_axes(axes, input.ndim) 

1345 num_axes = len(axes) 

1346 origins = _ni_support._normalize_sequence(origin, num_axes) 

1347 if footprint is None: 

1348 if size is None: 

1349 raise RuntimeError("no footprint or filter size provided") 

1350 sizes = _ni_support._normalize_sequence(size, num_axes) 

1351 footprint = numpy.ones(sizes, dtype=bool) 

1352 else: 

1353 footprint = numpy.asarray(footprint, dtype=bool) 

1354 if num_axes < input.ndim: 

1355 # set origin = 0 for any axes not being filtered 

1356 origins_temp = [0,] * input.ndim 

1357 for o, ax in zip(origins, axes): 

1358 origins_temp[ax] = o 

1359 origins = origins_temp 

1360 

1361 if not isinstance(mode, str) and isinstance(mode, Iterable): 

1362 # set mode = 'constant' for any axes not being filtered 

1363 modes = _ni_support._normalize_sequence(mode, num_axes) 

1364 modes_temp = ['constant'] * input.ndim 

1365 for m, ax in zip(modes, axes): 

1366 modes_temp[ax] = m 

1367 mode = modes_temp 

1368 

1369 # insert singleton dimension along any non-filtered axes 

1370 if footprint.ndim != num_axes: 

1371 raise RuntimeError("footprint array has incorrect shape") 

1372 footprint = numpy.expand_dims( 

1373 footprint, 

1374 tuple(ax for ax in range(input.ndim) if ax not in axes) 

1375 ) 

1376 fshape = [ii for ii in footprint.shape if ii > 0] 

1377 if len(fshape) != input.ndim: 

1378 raise RuntimeError('footprint array has incorrect shape.') 

1379 for origin, lenf in zip(origins, fshape): 

1380 if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf): 

1381 raise ValueError('invalid origin') 

1382 if not footprint.flags.contiguous: 

1383 footprint = footprint.copy() 

1384 filter_size = numpy.where(footprint, 1, 0).sum() 

1385 if operation == 'median': 

1386 rank = filter_size // 2 

1387 elif operation == 'percentile': 

1388 percentile = rank 

1389 if percentile < 0.0: 

1390 percentile += 100.0 

1391 if percentile < 0 or percentile > 100: 

1392 raise RuntimeError('invalid percentile') 

1393 if percentile == 100.0: 

1394 rank = filter_size - 1 

1395 else: 

1396 rank = int(float(filter_size) * percentile / 100.0) 

1397 if rank < 0: 

1398 rank += filter_size 

1399 if rank < 0 or rank >= filter_size: 

1400 raise RuntimeError('rank not within filter footprint size') 

1401 if rank == 0: 

1402 return minimum_filter(input, None, footprint, output, mode, cval, 

1403 origins, axes=None) 

1404 elif rank == filter_size - 1: 

1405 return maximum_filter(input, None, footprint, output, mode, cval, 

1406 origins, axes=None) 

1407 else: 

1408 output = _ni_support._get_output(output, input) 

1409 temp_needed = numpy.may_share_memory(input, output) 

1410 if temp_needed: 

1411 # input and output arrays cannot share memory 

1412 temp = output 

1413 output = _ni_support._get_output(output.dtype, input) 

1414 if not isinstance(mode, str) and isinstance(mode, Iterable): 

1415 raise RuntimeError( 

1416 "A sequence of modes is not supported by non-separable rank " 

1417 "filters") 

1418 mode = _ni_support._extend_mode_to_code(mode) 

1419 _nd_image.rank_filter(input, rank, footprint, output, mode, cval, 

1420 origins) 

1421 if temp_needed: 

1422 temp[...] = output 

1423 output = temp 

1424 return output 

1425 

1426 

1427@_ni_docstrings.docfiller 

1428def rank_filter(input, rank, size=None, footprint=None, output=None, 

1429 mode="reflect", cval=0.0, origin=0, *, axes=None): 

1430 """Calculate a multidimensional rank filter. 

1431 

1432 Parameters 

1433 ---------- 

1434 %(input)s 

1435 rank : int 

1436 The rank parameter may be less than zero, i.e., rank = -1 

1437 indicates the largest element. 

1438 %(size_foot)s 

1439 %(output)s 

1440 %(mode_reflect)s 

1441 %(cval)s 

1442 %(origin_multiple)s 

1443 axes : tuple of int or None, optional 

1444 If None, `input` is filtered along all axes. Otherwise, 

1445 `input` is filtered along the specified axes. 

1446 

1447 Returns 

1448 ------- 

1449 rank_filter : ndarray 

1450 Filtered array. Has the same shape as `input`. 

1451 

1452 Examples 

1453 -------- 

1454 >>> from scipy import ndimage, datasets 

1455 >>> import matplotlib.pyplot as plt 

1456 >>> fig = plt.figure() 

1457 >>> plt.gray() # show the filtered result in grayscale 

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

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

1460 >>> ascent = datasets.ascent() 

1461 >>> result = ndimage.rank_filter(ascent, rank=42, size=20) 

1462 >>> ax1.imshow(ascent) 

1463 >>> ax2.imshow(result) 

1464 >>> plt.show() 

1465 """ 

1466 rank = operator.index(rank) 

1467 return _rank_filter(input, rank, size, footprint, output, mode, cval, 

1468 origin, 'rank', axes=axes) 

1469 

1470 

1471@_ni_docstrings.docfiller 

1472def median_filter(input, size=None, footprint=None, output=None, 

1473 mode="reflect", cval=0.0, origin=0, *, axes=None): 

1474 """ 

1475 Calculate a multidimensional median filter. 

1476 

1477 Parameters 

1478 ---------- 

1479 %(input)s 

1480 %(size_foot)s 

1481 %(output)s 

1482 %(mode_reflect)s 

1483 %(cval)s 

1484 %(origin_multiple)s 

1485 axes : tuple of int or None, optional 

1486 If None, `input` is filtered along all axes. Otherwise, 

1487 `input` is filtered along the specified axes. 

1488 

1489 Returns 

1490 ------- 

1491 median_filter : ndarray 

1492 Filtered array. Has the same shape as `input`. 

1493 

1494 See Also 

1495 -------- 

1496 scipy.signal.medfilt2d 

1497 

1498 Notes 

1499 ----- 

1500 For 2-dimensional images with ``uint8``, ``float32`` or ``float64`` dtypes 

1501 the specialised function `scipy.signal.medfilt2d` may be faster. It is 

1502 however limited to constant mode with ``cval=0``. 

1503 

1504 Examples 

1505 -------- 

1506 >>> from scipy import ndimage, datasets 

1507 >>> import matplotlib.pyplot as plt 

1508 >>> fig = plt.figure() 

1509 >>> plt.gray() # show the filtered result in grayscale 

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

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

1512 >>> ascent = datasets.ascent() 

1513 >>> result = ndimage.median_filter(ascent, size=20) 

1514 >>> ax1.imshow(ascent) 

1515 >>> ax2.imshow(result) 

1516 >>> plt.show() 

1517 """ 

1518 return _rank_filter(input, 0, size, footprint, output, mode, cval, 

1519 origin, 'median', axes=axes) 

1520 

1521 

1522@_ni_docstrings.docfiller 

1523def percentile_filter(input, percentile, size=None, footprint=None, 

1524 output=None, mode="reflect", cval=0.0, origin=0, *, 

1525 axes=None): 

1526 """Calculate a multidimensional percentile filter. 

1527 

1528 Parameters 

1529 ---------- 

1530 %(input)s 

1531 percentile : scalar 

1532 The percentile parameter may be less than zero, i.e., 

1533 percentile = -20 equals percentile = 80 

1534 %(size_foot)s 

1535 %(output)s 

1536 %(mode_reflect)s 

1537 %(cval)s 

1538 %(origin_multiple)s 

1539 axes : tuple of int or None, optional 

1540 If None, `input` is filtered along all axes. Otherwise, 

1541 `input` is filtered along the specified axes. 

1542 

1543 Returns 

1544 ------- 

1545 percentile_filter : ndarray 

1546 Filtered array. Has the same shape as `input`. 

1547 

1548 Examples 

1549 -------- 

1550 >>> from scipy import ndimage, datasets 

1551 >>> import matplotlib.pyplot as plt 

1552 >>> fig = plt.figure() 

1553 >>> plt.gray() # show the filtered result in grayscale 

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

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

1556 >>> ascent = datasets.ascent() 

1557 >>> result = ndimage.percentile_filter(ascent, percentile=20, size=20) 

1558 >>> ax1.imshow(ascent) 

1559 >>> ax2.imshow(result) 

1560 >>> plt.show() 

1561 """ 

1562 return _rank_filter(input, percentile, size, footprint, output, mode, 

1563 cval, origin, 'percentile', axes=axes) 

1564 

1565 

1566@_ni_docstrings.docfiller 

1567def generic_filter1d(input, function, filter_size, axis=-1, 

1568 output=None, mode="reflect", cval=0.0, origin=0, 

1569 extra_arguments=(), extra_keywords=None): 

1570 """Calculate a 1-D filter along the given axis. 

1571 

1572 `generic_filter1d` iterates over the lines of the array, calling the 

1573 given function at each line. The arguments of the line are the 

1574 input line, and the output line. The input and output lines are 1-D 

1575 double arrays. The input line is extended appropriately according 

1576 to the filter size and origin. The output line must be modified 

1577 in-place with the result. 

1578 

1579 Parameters 

1580 ---------- 

1581 %(input)s 

1582 function : {callable, scipy.LowLevelCallable} 

1583 Function to apply along given axis. 

1584 filter_size : scalar 

1585 Length of the filter. 

1586 %(axis)s 

1587 %(output)s 

1588 %(mode_reflect)s 

1589 %(cval)s 

1590 %(origin)s 

1591 %(extra_arguments)s 

1592 %(extra_keywords)s 

1593 

1594 Notes 

1595 ----- 

1596 This function also accepts low-level callback functions with one of 

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

1598 

1599 .. code:: c 

1600 

1601 int function(double *input_line, npy_intp input_length, 

1602 double *output_line, npy_intp output_length, 

1603 void *user_data) 

1604 int function(double *input_line, intptr_t input_length, 

1605 double *output_line, intptr_t output_length, 

1606 void *user_data) 

1607 

1608 The calling function iterates over the lines of the input and output 

1609 arrays, calling the callback function at each line. The current line 

1610 is extended according to the border conditions set by the calling 

1611 function, and the result is copied into the array that is passed 

1612 through ``input_line``. The length of the input line (after extension) 

1613 is passed through ``input_length``. The callback function should apply 

1614 the filter and store the result in the array passed through 

1615 ``output_line``. The length of the output line is passed through 

1616 ``output_length``. ``user_data`` is the data pointer provided 

1617 to `scipy.LowLevelCallable` as-is. 

1618 

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

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

1621 normally set the python error status with an informative message 

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

1623 calling function. 

1624 

1625 In addition, some other low-level function pointer specifications 

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

1627 not be used in new code. 

1628 

1629 """ 

1630 if extra_keywords is None: 

1631 extra_keywords = {} 

1632 input = numpy.asarray(input) 

1633 if numpy.iscomplexobj(input): 

1634 raise TypeError('Complex type not supported') 

1635 output = _ni_support._get_output(output, input) 

1636 if filter_size < 1: 

1637 raise RuntimeError('invalid filter size') 

1638 axis = normalize_axis_index(axis, input.ndim) 

1639 if (filter_size // 2 + origin < 0) or (filter_size // 2 + origin >= 

1640 filter_size): 

1641 raise ValueError('invalid origin') 

1642 mode = _ni_support._extend_mode_to_code(mode) 

1643 _nd_image.generic_filter1d(input, function, filter_size, axis, output, 

1644 mode, cval, origin, extra_arguments, 

1645 extra_keywords) 

1646 return output 

1647 

1648 

1649@_ni_docstrings.docfiller 

1650def generic_filter(input, function, size=None, footprint=None, 

1651 output=None, mode="reflect", cval=0.0, origin=0, 

1652 extra_arguments=(), extra_keywords=None): 

1653 """Calculate a multidimensional filter using the given function. 

1654 

1655 At each element the provided function is called. The input values 

1656 within the filter footprint at that element are passed to the function 

1657 as a 1-D array of double values. 

1658 

1659 Parameters 

1660 ---------- 

1661 %(input)s 

1662 function : {callable, scipy.LowLevelCallable} 

1663 Function to apply at each element. 

1664 %(size_foot)s 

1665 %(output)s 

1666 %(mode_reflect)s 

1667 %(cval)s 

1668 %(origin_multiple)s 

1669 %(extra_arguments)s 

1670 %(extra_keywords)s 

1671 

1672 Notes 

1673 ----- 

1674 This function also accepts low-level callback functions with one of 

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

1676 

1677 .. code:: c 

1678 

1679 int callback(double *buffer, npy_intp filter_size, 

1680 double *return_value, void *user_data) 

1681 int callback(double *buffer, intptr_t filter_size, 

1682 double *return_value, void *user_data) 

1683 

1684 The calling function iterates over the elements of the input and 

1685 output arrays, calling the callback function at each element. The 

1686 elements within the footprint of the filter at the current element are 

1687 passed through the ``buffer`` parameter, and the number of elements 

1688 within the footprint through ``filter_size``. The calculated value is 

1689 returned in ``return_value``. ``user_data`` is the data pointer provided 

1690 to `scipy.LowLevelCallable` as-is. 

1691 

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

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

1694 normally set the python error status with an informative message 

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

1696 calling function. 

1697 

1698 In addition, some other low-level function pointer specifications 

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

1700 not be used in new code. 

1701 

1702 Examples 

1703 -------- 

1704 Import the necessary modules and load the example image used for 

1705 filtering. 

1706 

1707 >>> import numpy as np 

1708 >>> from scipy import datasets 

1709 >>> from scipy.ndimage import generic_filter 

1710 >>> import matplotlib.pyplot as plt 

1711 >>> ascent = datasets.ascent() 

1712 

1713 Compute a maximum filter with kernel size 10 by passing a simple NumPy 

1714 aggregation function as argument to `function`. 

1715 

1716 >>> maximum_filter_result = generic_filter(ascent, np.amax, [10, 10]) 

1717 

1718 While a maximmum filter could also directly be obtained using 

1719 `maximum_filter`, `generic_filter` allows generic Python function or 

1720 `scipy.LowLevelCallable` to be used as a filter. Here, we compute the 

1721 range between maximum and minimum value as an example for a kernel size 

1722 of 5. 

1723 

1724 >>> def custom_filter(image): 

1725 ... return np.amax(image) - np.amin(image) 

1726 >>> custom_filter_result = generic_filter(ascent, custom_filter, [5, 5]) 

1727 

1728 Plot the original and filtered images. 

1729 

1730 >>> fig, axes = plt.subplots(3, 1, figsize=(4, 12)) 

1731 >>> plt.gray() # show the filtered result in grayscale 

1732 >>> top, middle, bottom = axes 

1733 >>> for ax in axes: 

1734 ... ax.set_axis_off() # remove coordinate system 

1735 >>> top.imshow(ascent) 

1736 >>> top.set_title("Original image") 

1737 >>> middle.imshow(maximum_filter_result) 

1738 >>> middle.set_title("Maximum filter, Kernel: 10x10") 

1739 >>> bottom.imshow(custom_filter_result) 

1740 >>> bottom.set_title("Custom filter, Kernel: 5x5") 

1741 >>> fig.tight_layout() 

1742 

1743 """ 

1744 if (size is not None) and (footprint is not None): 

1745 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2) 

1746 if extra_keywords is None: 

1747 extra_keywords = {} 

1748 input = numpy.asarray(input) 

1749 if numpy.iscomplexobj(input): 

1750 raise TypeError('Complex type not supported') 

1751 origins = _ni_support._normalize_sequence(origin, input.ndim) 

1752 if footprint is None: 

1753 if size is None: 

1754 raise RuntimeError("no footprint or filter size provided") 

1755 sizes = _ni_support._normalize_sequence(size, input.ndim) 

1756 footprint = numpy.ones(sizes, dtype=bool) 

1757 else: 

1758 footprint = numpy.asarray(footprint, dtype=bool) 

1759 fshape = [ii for ii in footprint.shape if ii > 0] 

1760 if len(fshape) != input.ndim: 

1761 raise RuntimeError('filter footprint array has incorrect shape.') 

1762 for origin, lenf in zip(origins, fshape): 

1763 if (lenf // 2 + origin < 0) or (lenf // 2 + origin >= lenf): 

1764 raise ValueError('invalid origin') 

1765 if not footprint.flags.contiguous: 

1766 footprint = footprint.copy() 

1767 output = _ni_support._get_output(output, input) 

1768 mode = _ni_support._extend_mode_to_code(mode) 

1769 _nd_image.generic_filter(input, function, footprint, output, mode, 

1770 cval, origins, extra_arguments, extra_keywords) 

1771 return output