Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/ndimage/_morphology.py: 7%

413 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 warnings 

32import operator 

33 

34import numpy 

35from . import _ni_support 

36from . import _nd_image 

37from . import _filters 

38 

39__all__ = ['iterate_structure', 'generate_binary_structure', 'binary_erosion', 

40 'binary_dilation', 'binary_opening', 'binary_closing', 

41 'binary_hit_or_miss', 'binary_propagation', 'binary_fill_holes', 

42 'grey_erosion', 'grey_dilation', 'grey_opening', 'grey_closing', 

43 'morphological_gradient', 'morphological_laplace', 'white_tophat', 

44 'black_tophat', 'distance_transform_bf', 'distance_transform_cdt', 

45 'distance_transform_edt'] 

46 

47 

48def _center_is_true(structure, origin): 

49 structure = numpy.array(structure) 

50 coor = tuple([oo + ss // 2 for ss, oo in zip(structure.shape, 

51 origin)]) 

52 return bool(structure[coor]) 

53 

54 

55def iterate_structure(structure, iterations, origin=None): 

56 """ 

57 Iterate a structure by dilating it with itself. 

58 

59 Parameters 

60 ---------- 

61 structure : array_like 

62 Structuring element (an array of bools, for example), to be dilated with 

63 itself. 

64 iterations : int 

65 number of dilations performed on the structure with itself 

66 origin : optional 

67 If origin is None, only the iterated structure is returned. If 

68 not, a tuple of the iterated structure and the modified origin is 

69 returned. 

70 

71 Returns 

72 ------- 

73 iterate_structure : ndarray of bools 

74 A new structuring element obtained by dilating `structure` 

75 (`iterations` - 1) times with itself. 

76 

77 See also 

78 -------- 

79 generate_binary_structure 

80 

81 Examples 

82 -------- 

83 >>> from scipy import ndimage 

84 >>> struct = ndimage.generate_binary_structure(2, 1) 

85 >>> struct.astype(int) 

86 array([[0, 1, 0], 

87 [1, 1, 1], 

88 [0, 1, 0]]) 

89 >>> ndimage.iterate_structure(struct, 2).astype(int) 

90 array([[0, 0, 1, 0, 0], 

91 [0, 1, 1, 1, 0], 

92 [1, 1, 1, 1, 1], 

93 [0, 1, 1, 1, 0], 

94 [0, 0, 1, 0, 0]]) 

95 >>> ndimage.iterate_structure(struct, 3).astype(int) 

96 array([[0, 0, 0, 1, 0, 0, 0], 

97 [0, 0, 1, 1, 1, 0, 0], 

98 [0, 1, 1, 1, 1, 1, 0], 

99 [1, 1, 1, 1, 1, 1, 1], 

100 [0, 1, 1, 1, 1, 1, 0], 

101 [0, 0, 1, 1, 1, 0, 0], 

102 [0, 0, 0, 1, 0, 0, 0]]) 

103 

104 """ 

105 structure = numpy.asarray(structure) 

106 if iterations < 2: 

107 return structure.copy() 

108 ni = iterations - 1 

109 shape = [ii + ni * (ii - 1) for ii in structure.shape] 

110 pos = [ni * (structure.shape[ii] // 2) for ii in range(len(shape))] 

111 slc = tuple(slice(pos[ii], pos[ii] + structure.shape[ii], None) 

112 for ii in range(len(shape))) 

113 out = numpy.zeros(shape, bool) 

114 out[slc] = structure != 0 

115 out = binary_dilation(out, structure, iterations=ni) 

116 if origin is None: 

117 return out 

118 else: 

119 origin = _ni_support._normalize_sequence(origin, structure.ndim) 

120 origin = [iterations * o for o in origin] 

121 return out, origin 

122 

123 

124def generate_binary_structure(rank, connectivity): 

125 """ 

126 Generate a binary structure for binary morphological operations. 

127 

128 Parameters 

129 ---------- 

130 rank : int 

131 Number of dimensions of the array to which the structuring element 

132 will be applied, as returned by `np.ndim`. 

133 connectivity : int 

134 `connectivity` determines which elements of the output array belong 

135 to the structure, i.e., are considered as neighbors of the central 

136 element. Elements up to a squared distance of `connectivity` from 

137 the center are considered neighbors. `connectivity` may range from 1 

138 (no diagonal elements are neighbors) to `rank` (all elements are 

139 neighbors). 

140 

141 Returns 

142 ------- 

143 output : ndarray of bools 

144 Structuring element which may be used for binary morphological 

145 operations, with `rank` dimensions and all dimensions equal to 3. 

146 

147 See also 

148 -------- 

149 iterate_structure, binary_dilation, binary_erosion 

150 

151 Notes 

152 ----- 

153 `generate_binary_structure` can only create structuring elements with 

154 dimensions equal to 3, i.e., minimal dimensions. For larger structuring 

155 elements, that are useful e.g., for eroding large objects, one may either 

156 use `iterate_structure`, or create directly custom arrays with 

157 numpy functions such as `numpy.ones`. 

158 

159 Examples 

160 -------- 

161 >>> from scipy import ndimage 

162 >>> import numpy as np 

163 >>> struct = ndimage.generate_binary_structure(2, 1) 

164 >>> struct 

165 array([[False, True, False], 

166 [ True, True, True], 

167 [False, True, False]], dtype=bool) 

168 >>> a = np.zeros((5,5)) 

169 >>> a[2, 2] = 1 

170 >>> a 

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

172 [ 0., 0., 0., 0., 0.], 

173 [ 0., 0., 1., 0., 0.], 

174 [ 0., 0., 0., 0., 0.], 

175 [ 0., 0., 0., 0., 0.]]) 

176 >>> b = ndimage.binary_dilation(a, structure=struct).astype(a.dtype) 

177 >>> b 

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

179 [ 0., 0., 1., 0., 0.], 

180 [ 0., 1., 1., 1., 0.], 

181 [ 0., 0., 1., 0., 0.], 

182 [ 0., 0., 0., 0., 0.]]) 

183 >>> ndimage.binary_dilation(b, structure=struct).astype(a.dtype) 

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

185 [ 0., 1., 1., 1., 0.], 

186 [ 1., 1., 1., 1., 1.], 

187 [ 0., 1., 1., 1., 0.], 

188 [ 0., 0., 1., 0., 0.]]) 

189 >>> struct = ndimage.generate_binary_structure(2, 2) 

190 >>> struct 

191 array([[ True, True, True], 

192 [ True, True, True], 

193 [ True, True, True]], dtype=bool) 

194 >>> struct = ndimage.generate_binary_structure(3, 1) 

195 >>> struct # no diagonal elements 

196 array([[[False, False, False], 

197 [False, True, False], 

198 [False, False, False]], 

199 [[False, True, False], 

200 [ True, True, True], 

201 [False, True, False]], 

202 [[False, False, False], 

203 [False, True, False], 

204 [False, False, False]]], dtype=bool) 

205 

206 """ 

207 if connectivity < 1: 

208 connectivity = 1 

209 if rank < 1: 

210 return numpy.array(True, dtype=bool) 

211 output = numpy.fabs(numpy.indices([3] * rank) - 1) 

212 output = numpy.add.reduce(output, 0) 

213 return output <= connectivity 

214 

215 

216def _binary_erosion(input, structure, iterations, mask, output, 

217 border_value, origin, invert, brute_force): 

218 try: 

219 iterations = operator.index(iterations) 

220 except TypeError as e: 

221 raise TypeError('iterations parameter should be an integer') from e 

222 

223 input = numpy.asarray(input) 

224 if numpy.iscomplexobj(input): 

225 raise TypeError('Complex type not supported') 

226 if structure is None: 

227 structure = generate_binary_structure(input.ndim, 1) 

228 else: 

229 structure = numpy.asarray(structure, dtype=bool) 

230 if structure.ndim != input.ndim: 

231 raise RuntimeError('structure and input must have same dimensionality') 

232 if not structure.flags.contiguous: 

233 structure = structure.copy() 

234 if numpy.prod(structure.shape, axis=0) < 1: 

235 raise RuntimeError('structure must not be empty') 

236 if mask is not None: 

237 mask = numpy.asarray(mask) 

238 if mask.shape != input.shape: 

239 raise RuntimeError('mask and input must have equal sizes') 

240 origin = _ni_support._normalize_sequence(origin, input.ndim) 

241 cit = _center_is_true(structure, origin) 

242 if isinstance(output, numpy.ndarray): 

243 if numpy.iscomplexobj(output): 

244 raise TypeError('Complex output type not supported') 

245 else: 

246 output = bool 

247 output = _ni_support._get_output(output, input) 

248 temp_needed = numpy.may_share_memory(input, output) 

249 if temp_needed: 

250 # input and output arrays cannot share memory 

251 temp = output 

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

253 if iterations == 1: 

254 _nd_image.binary_erosion(input, structure, mask, output, 

255 border_value, origin, invert, cit, 0) 

256 elif cit and not brute_force: 

257 changed, coordinate_list = _nd_image.binary_erosion( 

258 input, structure, mask, output, 

259 border_value, origin, invert, cit, 1) 

260 structure = structure[tuple([slice(None, None, -1)] * 

261 structure.ndim)] 

262 for ii in range(len(origin)): 

263 origin[ii] = -origin[ii] 

264 if not structure.shape[ii] & 1: 

265 origin[ii] -= 1 

266 if mask is not None: 

267 mask = numpy.asarray(mask, dtype=numpy.int8) 

268 if not structure.flags.contiguous: 

269 structure = structure.copy() 

270 _nd_image.binary_erosion2(output, structure, mask, iterations - 1, 

271 origin, invert, coordinate_list) 

272 else: 

273 tmp_in = numpy.empty_like(input, dtype=bool) 

274 tmp_out = output 

275 if iterations >= 1 and not iterations & 1: 

276 tmp_in, tmp_out = tmp_out, tmp_in 

277 changed = _nd_image.binary_erosion( 

278 input, structure, mask, tmp_out, 

279 border_value, origin, invert, cit, 0) 

280 ii = 1 

281 while ii < iterations or (iterations < 1 and changed): 

282 tmp_in, tmp_out = tmp_out, tmp_in 

283 changed = _nd_image.binary_erosion( 

284 tmp_in, structure, mask, tmp_out, 

285 border_value, origin, invert, cit, 0) 

286 ii += 1 

287 if temp_needed: 

288 temp[...] = output 

289 output = temp 

290 return output 

291 

292 

293def binary_erosion(input, structure=None, iterations=1, mask=None, output=None, 

294 border_value=0, origin=0, brute_force=False): 

295 """ 

296 Multidimensional binary erosion with a given structuring element. 

297 

298 Binary erosion is a mathematical morphology operation used for image 

299 processing. 

300 

301 Parameters 

302 ---------- 

303 input : array_like 

304 Binary image to be eroded. Non-zero (True) elements form 

305 the subset to be eroded. 

306 structure : array_like, optional 

307 Structuring element used for the erosion. Non-zero elements are 

308 considered True. If no structuring element is provided, an element 

309 is generated with a square connectivity equal to one. 

310 iterations : int, optional 

311 The erosion is repeated `iterations` times (one, by default). 

312 If iterations is less than 1, the erosion is repeated until the 

313 result does not change anymore. 

314 mask : array_like, optional 

315 If a mask is given, only those elements with a True value at 

316 the corresponding mask element are modified at each iteration. 

317 output : ndarray, optional 

318 Array of the same shape as input, into which the output is placed. 

319 By default, a new array is created. 

320 border_value : int (cast to 0 or 1), optional 

321 Value at the border in the output array. 

322 origin : int or tuple of ints, optional 

323 Placement of the filter, by default 0. 

324 brute_force : boolean, optional 

325 Memory condition: if False, only the pixels whose value was changed in 

326 the last iteration are tracked as candidates to be updated (eroded) in 

327 the current iteration; if True all pixels are considered as candidates 

328 for erosion, regardless of what happened in the previous iteration. 

329 False by default. 

330 

331 Returns 

332 ------- 

333 binary_erosion : ndarray of bools 

334 Erosion of the input by the structuring element. 

335 

336 See also 

337 -------- 

338 grey_erosion, binary_dilation, binary_closing, binary_opening, 

339 generate_binary_structure 

340 

341 Notes 

342 ----- 

343 Erosion [1]_ is a mathematical morphology operation [2]_ that uses a 

344 structuring element for shrinking the shapes in an image. The binary 

345 erosion of an image by a structuring element is the locus of the points 

346 where a superimposition of the structuring element centered on the point 

347 is entirely contained in the set of non-zero elements of the image. 

348 

349 References 

350 ---------- 

351 .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29 

352 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology 

353 

354 Examples 

355 -------- 

356 >>> from scipy import ndimage 

357 >>> import numpy as np 

358 >>> a = np.zeros((7,7), dtype=int) 

359 >>> a[1:6, 2:5] = 1 

360 >>> a 

361 array([[0, 0, 0, 0, 0, 0, 0], 

362 [0, 0, 1, 1, 1, 0, 0], 

363 [0, 0, 1, 1, 1, 0, 0], 

364 [0, 0, 1, 1, 1, 0, 0], 

365 [0, 0, 1, 1, 1, 0, 0], 

366 [0, 0, 1, 1, 1, 0, 0], 

367 [0, 0, 0, 0, 0, 0, 0]]) 

368 >>> ndimage.binary_erosion(a).astype(a.dtype) 

369 array([[0, 0, 0, 0, 0, 0, 0], 

370 [0, 0, 0, 0, 0, 0, 0], 

371 [0, 0, 0, 1, 0, 0, 0], 

372 [0, 0, 0, 1, 0, 0, 0], 

373 [0, 0, 0, 1, 0, 0, 0], 

374 [0, 0, 0, 0, 0, 0, 0], 

375 [0, 0, 0, 0, 0, 0, 0]]) 

376 >>> #Erosion removes objects smaller than the structure 

377 >>> ndimage.binary_erosion(a, structure=np.ones((5,5))).astype(a.dtype) 

378 array([[0, 0, 0, 0, 0, 0, 0], 

379 [0, 0, 0, 0, 0, 0, 0], 

380 [0, 0, 0, 0, 0, 0, 0], 

381 [0, 0, 0, 0, 0, 0, 0], 

382 [0, 0, 0, 0, 0, 0, 0], 

383 [0, 0, 0, 0, 0, 0, 0], 

384 [0, 0, 0, 0, 0, 0, 0]]) 

385 

386 """ 

387 return _binary_erosion(input, structure, iterations, mask, 

388 output, border_value, origin, 0, brute_force) 

389 

390 

391def binary_dilation(input, structure=None, iterations=1, mask=None, 

392 output=None, border_value=0, origin=0, 

393 brute_force=False): 

394 """ 

395 Multidimensional binary dilation with the given structuring element. 

396 

397 Parameters 

398 ---------- 

399 input : array_like 

400 Binary array_like to be dilated. Non-zero (True) elements form 

401 the subset to be dilated. 

402 structure : array_like, optional 

403 Structuring element used for the dilation. Non-zero elements are 

404 considered True. If no structuring element is provided an element 

405 is generated with a square connectivity equal to one. 

406 iterations : int, optional 

407 The dilation is repeated `iterations` times (one, by default). 

408 If iterations is less than 1, the dilation is repeated until the 

409 result does not change anymore. Only an integer of iterations is 

410 accepted. 

411 mask : array_like, optional 

412 If a mask is given, only those elements with a True value at 

413 the corresponding mask element are modified at each iteration. 

414 output : ndarray, optional 

415 Array of the same shape as input, into which the output is placed. 

416 By default, a new array is created. 

417 border_value : int (cast to 0 or 1), optional 

418 Value at the border in the output array. 

419 origin : int or tuple of ints, optional 

420 Placement of the filter, by default 0. 

421 brute_force : boolean, optional 

422 Memory condition: if False, only the pixels whose value was changed in 

423 the last iteration are tracked as candidates to be updated (dilated) 

424 in the current iteration; if True all pixels are considered as 

425 candidates for dilation, regardless of what happened in the previous 

426 iteration. False by default. 

427 

428 Returns 

429 ------- 

430 binary_dilation : ndarray of bools 

431 Dilation of the input by the structuring element. 

432 

433 See also 

434 -------- 

435 grey_dilation, binary_erosion, binary_closing, binary_opening, 

436 generate_binary_structure 

437 

438 Notes 

439 ----- 

440 Dilation [1]_ is a mathematical morphology operation [2]_ that uses a 

441 structuring element for expanding the shapes in an image. The binary 

442 dilation of an image by a structuring element is the locus of the points 

443 covered by the structuring element, when its center lies within the 

444 non-zero points of the image. 

445 

446 References 

447 ---------- 

448 .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29 

449 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology 

450 

451 Examples 

452 -------- 

453 >>> from scipy import ndimage 

454 >>> import numpy as np 

455 >>> a = np.zeros((5, 5)) 

456 >>> a[2, 2] = 1 

457 >>> a 

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

459 [ 0., 0., 0., 0., 0.], 

460 [ 0., 0., 1., 0., 0.], 

461 [ 0., 0., 0., 0., 0.], 

462 [ 0., 0., 0., 0., 0.]]) 

463 >>> ndimage.binary_dilation(a) 

464 array([[False, False, False, False, False], 

465 [False, False, True, False, False], 

466 [False, True, True, True, False], 

467 [False, False, True, False, False], 

468 [False, False, False, False, False]], dtype=bool) 

469 >>> ndimage.binary_dilation(a).astype(a.dtype) 

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

471 [ 0., 0., 1., 0., 0.], 

472 [ 0., 1., 1., 1., 0.], 

473 [ 0., 0., 1., 0., 0.], 

474 [ 0., 0., 0., 0., 0.]]) 

475 >>> # 3x3 structuring element with connectivity 1, used by default 

476 >>> struct1 = ndimage.generate_binary_structure(2, 1) 

477 >>> struct1 

478 array([[False, True, False], 

479 [ True, True, True], 

480 [False, True, False]], dtype=bool) 

481 >>> # 3x3 structuring element with connectivity 2 

482 >>> struct2 = ndimage.generate_binary_structure(2, 2) 

483 >>> struct2 

484 array([[ True, True, True], 

485 [ True, True, True], 

486 [ True, True, True]], dtype=bool) 

487 >>> ndimage.binary_dilation(a, structure=struct1).astype(a.dtype) 

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

489 [ 0., 0., 1., 0., 0.], 

490 [ 0., 1., 1., 1., 0.], 

491 [ 0., 0., 1., 0., 0.], 

492 [ 0., 0., 0., 0., 0.]]) 

493 >>> ndimage.binary_dilation(a, structure=struct2).astype(a.dtype) 

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

495 [ 0., 1., 1., 1., 0.], 

496 [ 0., 1., 1., 1., 0.], 

497 [ 0., 1., 1., 1., 0.], 

498 [ 0., 0., 0., 0., 0.]]) 

499 >>> ndimage.binary_dilation(a, structure=struct1,\\ 

500 ... iterations=2).astype(a.dtype) 

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

502 [ 0., 1., 1., 1., 0.], 

503 [ 1., 1., 1., 1., 1.], 

504 [ 0., 1., 1., 1., 0.], 

505 [ 0., 0., 1., 0., 0.]]) 

506 

507 """ 

508 input = numpy.asarray(input) 

509 if structure is None: 

510 structure = generate_binary_structure(input.ndim, 1) 

511 origin = _ni_support._normalize_sequence(origin, input.ndim) 

512 structure = numpy.asarray(structure) 

513 structure = structure[tuple([slice(None, None, -1)] * 

514 structure.ndim)] 

515 for ii in range(len(origin)): 

516 origin[ii] = -origin[ii] 

517 if not structure.shape[ii] & 1: 

518 origin[ii] -= 1 

519 

520 return _binary_erosion(input, structure, iterations, mask, 

521 output, border_value, origin, 1, brute_force) 

522 

523 

524def binary_opening(input, structure=None, iterations=1, output=None, 

525 origin=0, mask=None, border_value=0, brute_force=False): 

526 """ 

527 Multidimensional binary opening with the given structuring element. 

528 

529 The *opening* of an input image by a structuring element is the 

530 *dilation* of the *erosion* of the image by the structuring element. 

531 

532 Parameters 

533 ---------- 

534 input : array_like 

535 Binary array_like to be opened. Non-zero (True) elements form 

536 the subset to be opened. 

537 structure : array_like, optional 

538 Structuring element used for the opening. Non-zero elements are 

539 considered True. If no structuring element is provided an element 

540 is generated with a square connectivity equal to one (i.e., only 

541 nearest neighbors are connected to the center, diagonally-connected 

542 elements are not considered neighbors). 

543 iterations : int, optional 

544 The erosion step of the opening, then the dilation step are each 

545 repeated `iterations` times (one, by default). If `iterations` is 

546 less than 1, each operation is repeated until the result does 

547 not change anymore. Only an integer of iterations is accepted. 

548 output : ndarray, optional 

549 Array of the same shape as input, into which the output is placed. 

550 By default, a new array is created. 

551 origin : int or tuple of ints, optional 

552 Placement of the filter, by default 0. 

553 mask : array_like, optional 

554 If a mask is given, only those elements with a True value at 

555 the corresponding mask element are modified at each iteration. 

556 

557 .. versionadded:: 1.1.0 

558 border_value : int (cast to 0 or 1), optional 

559 Value at the border in the output array. 

560 

561 .. versionadded:: 1.1.0 

562 brute_force : boolean, optional 

563 Memory condition: if False, only the pixels whose value was changed in 

564 the last iteration are tracked as candidates to be updated in the 

565 current iteration; if true all pixels are considered as candidates for 

566 update, regardless of what happened in the previous iteration. 

567 False by default. 

568 

569 .. versionadded:: 1.1.0 

570 

571 Returns 

572 ------- 

573 binary_opening : ndarray of bools 

574 Opening of the input by the structuring element. 

575 

576 See also 

577 -------- 

578 grey_opening, binary_closing, binary_erosion, binary_dilation, 

579 generate_binary_structure 

580 

581 Notes 

582 ----- 

583 *Opening* [1]_ is a mathematical morphology operation [2]_ that 

584 consists in the succession of an erosion and a dilation of the 

585 input with the same structuring element. Opening, therefore, removes 

586 objects smaller than the structuring element. 

587 

588 Together with *closing* (`binary_closing`), opening can be used for 

589 noise removal. 

590 

591 References 

592 ---------- 

593 .. [1] https://en.wikipedia.org/wiki/Opening_%28morphology%29 

594 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology 

595 

596 Examples 

597 -------- 

598 >>> from scipy import ndimage 

599 >>> import numpy as np 

600 >>> a = np.zeros((5,5), dtype=int) 

601 >>> a[1:4, 1:4] = 1; a[4, 4] = 1 

602 >>> a 

603 array([[0, 0, 0, 0, 0], 

604 [0, 1, 1, 1, 0], 

605 [0, 1, 1, 1, 0], 

606 [0, 1, 1, 1, 0], 

607 [0, 0, 0, 0, 1]]) 

608 >>> # Opening removes small objects 

609 >>> ndimage.binary_opening(a, structure=np.ones((3,3))).astype(int) 

610 array([[0, 0, 0, 0, 0], 

611 [0, 1, 1, 1, 0], 

612 [0, 1, 1, 1, 0], 

613 [0, 1, 1, 1, 0], 

614 [0, 0, 0, 0, 0]]) 

615 >>> # Opening can also smooth corners 

616 >>> ndimage.binary_opening(a).astype(int) 

617 array([[0, 0, 0, 0, 0], 

618 [0, 0, 1, 0, 0], 

619 [0, 1, 1, 1, 0], 

620 [0, 0, 1, 0, 0], 

621 [0, 0, 0, 0, 0]]) 

622 >>> # Opening is the dilation of the erosion of the input 

623 >>> ndimage.binary_erosion(a).astype(int) 

624 array([[0, 0, 0, 0, 0], 

625 [0, 0, 0, 0, 0], 

626 [0, 0, 1, 0, 0], 

627 [0, 0, 0, 0, 0], 

628 [0, 0, 0, 0, 0]]) 

629 >>> ndimage.binary_dilation(ndimage.binary_erosion(a)).astype(int) 

630 array([[0, 0, 0, 0, 0], 

631 [0, 0, 1, 0, 0], 

632 [0, 1, 1, 1, 0], 

633 [0, 0, 1, 0, 0], 

634 [0, 0, 0, 0, 0]]) 

635 

636 """ 

637 input = numpy.asarray(input) 

638 if structure is None: 

639 rank = input.ndim 

640 structure = generate_binary_structure(rank, 1) 

641 

642 tmp = binary_erosion(input, structure, iterations, mask, None, 

643 border_value, origin, brute_force) 

644 return binary_dilation(tmp, structure, iterations, mask, output, 

645 border_value, origin, brute_force) 

646 

647 

648def binary_closing(input, structure=None, iterations=1, output=None, 

649 origin=0, mask=None, border_value=0, brute_force=False): 

650 """ 

651 Multidimensional binary closing with the given structuring element. 

652 

653 The *closing* of an input image by a structuring element is the 

654 *erosion* of the *dilation* of the image by the structuring element. 

655 

656 Parameters 

657 ---------- 

658 input : array_like 

659 Binary array_like to be closed. Non-zero (True) elements form 

660 the subset to be closed. 

661 structure : array_like, optional 

662 Structuring element used for the closing. Non-zero elements are 

663 considered True. If no structuring element is provided an element 

664 is generated with a square connectivity equal to one (i.e., only 

665 nearest neighbors are connected to the center, diagonally-connected 

666 elements are not considered neighbors). 

667 iterations : int, optional 

668 The dilation step of the closing, then the erosion step are each 

669 repeated `iterations` times (one, by default). If iterations is 

670 less than 1, each operations is repeated until the result does 

671 not change anymore. Only an integer of iterations is accepted. 

672 output : ndarray, optional 

673 Array of the same shape as input, into which the output is placed. 

674 By default, a new array is created. 

675 origin : int or tuple of ints, optional 

676 Placement of the filter, by default 0. 

677 mask : array_like, optional 

678 If a mask is given, only those elements with a True value at 

679 the corresponding mask element are modified at each iteration. 

680 

681 .. versionadded:: 1.1.0 

682 border_value : int (cast to 0 or 1), optional 

683 Value at the border in the output array. 

684 

685 .. versionadded:: 1.1.0 

686 brute_force : boolean, optional 

687 Memory condition: if False, only the pixels whose value was changed in 

688 the last iteration are tracked as candidates to be updated in the 

689 current iteration; if true al pixels are considered as candidates for 

690 update, regardless of what happened in the previous iteration. 

691 False by default. 

692 

693 .. versionadded:: 1.1.0 

694 

695 Returns 

696 ------- 

697 binary_closing : ndarray of bools 

698 Closing of the input by the structuring element. 

699 

700 See also 

701 -------- 

702 grey_closing, binary_opening, binary_dilation, binary_erosion, 

703 generate_binary_structure 

704 

705 Notes 

706 ----- 

707 *Closing* [1]_ is a mathematical morphology operation [2]_ that 

708 consists in the succession of a dilation and an erosion of the 

709 input with the same structuring element. Closing therefore fills 

710 holes smaller than the structuring element. 

711 

712 Together with *opening* (`binary_opening`), closing can be used for 

713 noise removal. 

714 

715 References 

716 ---------- 

717 .. [1] https://en.wikipedia.org/wiki/Closing_%28morphology%29 

718 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology 

719 

720 Examples 

721 -------- 

722 >>> from scipy import ndimage 

723 >>> import numpy as np 

724 >>> a = np.zeros((5,5), dtype=int) 

725 >>> a[1:-1, 1:-1] = 1; a[2,2] = 0 

726 >>> a 

727 array([[0, 0, 0, 0, 0], 

728 [0, 1, 1, 1, 0], 

729 [0, 1, 0, 1, 0], 

730 [0, 1, 1, 1, 0], 

731 [0, 0, 0, 0, 0]]) 

732 >>> # Closing removes small holes 

733 >>> ndimage.binary_closing(a).astype(int) 

734 array([[0, 0, 0, 0, 0], 

735 [0, 1, 1, 1, 0], 

736 [0, 1, 1, 1, 0], 

737 [0, 1, 1, 1, 0], 

738 [0, 0, 0, 0, 0]]) 

739 >>> # Closing is the erosion of the dilation of the input 

740 >>> ndimage.binary_dilation(a).astype(int) 

741 array([[0, 1, 1, 1, 0], 

742 [1, 1, 1, 1, 1], 

743 [1, 1, 1, 1, 1], 

744 [1, 1, 1, 1, 1], 

745 [0, 1, 1, 1, 0]]) 

746 >>> ndimage.binary_erosion(ndimage.binary_dilation(a)).astype(int) 

747 array([[0, 0, 0, 0, 0], 

748 [0, 1, 1, 1, 0], 

749 [0, 1, 1, 1, 0], 

750 [0, 1, 1, 1, 0], 

751 [0, 0, 0, 0, 0]]) 

752 

753 

754 >>> a = np.zeros((7,7), dtype=int) 

755 >>> a[1:6, 2:5] = 1; a[1:3,3] = 0 

756 >>> a 

757 array([[0, 0, 0, 0, 0, 0, 0], 

758 [0, 0, 1, 0, 1, 0, 0], 

759 [0, 0, 1, 0, 1, 0, 0], 

760 [0, 0, 1, 1, 1, 0, 0], 

761 [0, 0, 1, 1, 1, 0, 0], 

762 [0, 0, 1, 1, 1, 0, 0], 

763 [0, 0, 0, 0, 0, 0, 0]]) 

764 >>> # In addition to removing holes, closing can also 

765 >>> # coarsen boundaries with fine hollows. 

766 >>> ndimage.binary_closing(a).astype(int) 

767 array([[0, 0, 0, 0, 0, 0, 0], 

768 [0, 0, 1, 0, 1, 0, 0], 

769 [0, 0, 1, 1, 1, 0, 0], 

770 [0, 0, 1, 1, 1, 0, 0], 

771 [0, 0, 1, 1, 1, 0, 0], 

772 [0, 0, 1, 1, 1, 0, 0], 

773 [0, 0, 0, 0, 0, 0, 0]]) 

774 >>> ndimage.binary_closing(a, structure=np.ones((2,2))).astype(int) 

775 array([[0, 0, 0, 0, 0, 0, 0], 

776 [0, 0, 1, 1, 1, 0, 0], 

777 [0, 0, 1, 1, 1, 0, 0], 

778 [0, 0, 1, 1, 1, 0, 0], 

779 [0, 0, 1, 1, 1, 0, 0], 

780 [0, 0, 1, 1, 1, 0, 0], 

781 [0, 0, 0, 0, 0, 0, 0]]) 

782 

783 """ 

784 input = numpy.asarray(input) 

785 if structure is None: 

786 rank = input.ndim 

787 structure = generate_binary_structure(rank, 1) 

788 

789 tmp = binary_dilation(input, structure, iterations, mask, None, 

790 border_value, origin, brute_force) 

791 return binary_erosion(tmp, structure, iterations, mask, output, 

792 border_value, origin, brute_force) 

793 

794 

795def binary_hit_or_miss(input, structure1=None, structure2=None, 

796 output=None, origin1=0, origin2=None): 

797 """ 

798 Multidimensional binary hit-or-miss transform. 

799 

800 The hit-or-miss transform finds the locations of a given pattern 

801 inside the input image. 

802 

803 Parameters 

804 ---------- 

805 input : array_like (cast to booleans) 

806 Binary image where a pattern is to be detected. 

807 structure1 : array_like (cast to booleans), optional 

808 Part of the structuring element to be fitted to the foreground 

809 (non-zero elements) of `input`. If no value is provided, a 

810 structure of square connectivity 1 is chosen. 

811 structure2 : array_like (cast to booleans), optional 

812 Second part of the structuring element that has to miss completely 

813 the foreground. If no value is provided, the complementary of 

814 `structure1` is taken. 

815 output : ndarray, optional 

816 Array of the same shape as input, into which the output is placed. 

817 By default, a new array is created. 

818 origin1 : int or tuple of ints, optional 

819 Placement of the first part of the structuring element `structure1`, 

820 by default 0 for a centered structure. 

821 origin2 : int or tuple of ints, optional 

822 Placement of the second part of the structuring element `structure2`, 

823 by default 0 for a centered structure. If a value is provided for 

824 `origin1` and not for `origin2`, then `origin2` is set to `origin1`. 

825 

826 Returns 

827 ------- 

828 binary_hit_or_miss : ndarray 

829 Hit-or-miss transform of `input` with the given structuring 

830 element (`structure1`, `structure2`). 

831 

832 See also 

833 -------- 

834 binary_erosion 

835 

836 References 

837 ---------- 

838 .. [1] https://en.wikipedia.org/wiki/Hit-or-miss_transform 

839 

840 Examples 

841 -------- 

842 >>> from scipy import ndimage 

843 >>> import numpy as np 

844 >>> a = np.zeros((7,7), dtype=int) 

845 >>> a[1, 1] = 1; a[2:4, 2:4] = 1; a[4:6, 4:6] = 1 

846 >>> a 

847 array([[0, 0, 0, 0, 0, 0, 0], 

848 [0, 1, 0, 0, 0, 0, 0], 

849 [0, 0, 1, 1, 0, 0, 0], 

850 [0, 0, 1, 1, 0, 0, 0], 

851 [0, 0, 0, 0, 1, 1, 0], 

852 [0, 0, 0, 0, 1, 1, 0], 

853 [0, 0, 0, 0, 0, 0, 0]]) 

854 >>> structure1 = np.array([[1, 0, 0], [0, 1, 1], [0, 1, 1]]) 

855 >>> structure1 

856 array([[1, 0, 0], 

857 [0, 1, 1], 

858 [0, 1, 1]]) 

859 >>> # Find the matches of structure1 in the array a 

860 >>> ndimage.binary_hit_or_miss(a, structure1=structure1).astype(int) 

861 array([[0, 0, 0, 0, 0, 0, 0], 

862 [0, 0, 0, 0, 0, 0, 0], 

863 [0, 0, 1, 0, 0, 0, 0], 

864 [0, 0, 0, 0, 0, 0, 0], 

865 [0, 0, 0, 0, 1, 0, 0], 

866 [0, 0, 0, 0, 0, 0, 0], 

867 [0, 0, 0, 0, 0, 0, 0]]) 

868 >>> # Change the origin of the filter 

869 >>> # origin1=1 is equivalent to origin1=(1,1) here 

870 >>> ndimage.binary_hit_or_miss(a, structure1=structure1,\\ 

871 ... origin1=1).astype(int) 

872 array([[0, 0, 0, 0, 0, 0, 0], 

873 [0, 0, 0, 0, 0, 0, 0], 

874 [0, 0, 0, 0, 0, 0, 0], 

875 [0, 0, 0, 1, 0, 0, 0], 

876 [0, 0, 0, 0, 0, 0, 0], 

877 [0, 0, 0, 0, 0, 1, 0], 

878 [0, 0, 0, 0, 0, 0, 0]]) 

879 

880 """ 

881 input = numpy.asarray(input) 

882 if structure1 is None: 

883 structure1 = generate_binary_structure(input.ndim, 1) 

884 if structure2 is None: 

885 structure2 = numpy.logical_not(structure1) 

886 origin1 = _ni_support._normalize_sequence(origin1, input.ndim) 

887 if origin2 is None: 

888 origin2 = origin1 

889 else: 

890 origin2 = _ni_support._normalize_sequence(origin2, input.ndim) 

891 

892 tmp1 = _binary_erosion(input, structure1, 1, None, None, 0, origin1, 

893 0, False) 

894 inplace = isinstance(output, numpy.ndarray) 

895 result = _binary_erosion(input, structure2, 1, None, output, 0, 

896 origin2, 1, False) 

897 if inplace: 

898 numpy.logical_not(output, output) 

899 numpy.logical_and(tmp1, output, output) 

900 else: 

901 numpy.logical_not(result, result) 

902 return numpy.logical_and(tmp1, result) 

903 

904 

905def binary_propagation(input, structure=None, mask=None, 

906 output=None, border_value=0, origin=0): 

907 """ 

908 Multidimensional binary propagation with the given structuring element. 

909 

910 Parameters 

911 ---------- 

912 input : array_like 

913 Binary image to be propagated inside `mask`. 

914 structure : array_like, optional 

915 Structuring element used in the successive dilations. The output 

916 may depend on the structuring element, especially if `mask` has 

917 several connex components. If no structuring element is 

918 provided, an element is generated with a squared connectivity equal 

919 to one. 

920 mask : array_like, optional 

921 Binary mask defining the region into which `input` is allowed to 

922 propagate. 

923 output : ndarray, optional 

924 Array of the same shape as input, into which the output is placed. 

925 By default, a new array is created. 

926 border_value : int (cast to 0 or 1), optional 

927 Value at the border in the output array. 

928 origin : int or tuple of ints, optional 

929 Placement of the filter, by default 0. 

930 

931 Returns 

932 ------- 

933 binary_propagation : ndarray 

934 Binary propagation of `input` inside `mask`. 

935 

936 Notes 

937 ----- 

938 This function is functionally equivalent to calling binary_dilation 

939 with the number of iterations less than one: iterative dilation until 

940 the result does not change anymore. 

941 

942 The succession of an erosion and propagation inside the original image 

943 can be used instead of an *opening* for deleting small objects while 

944 keeping the contours of larger objects untouched. 

945 

946 References 

947 ---------- 

948 .. [1] http://cmm.ensmp.fr/~serra/cours/pdf/en/ch6en.pdf, slide 15. 

949 .. [2] I.T. Young, J.J. Gerbrands, and L.J. van Vliet, "Fundamentals of 

950 image processing", 1998 

951 ftp://qiftp.tudelft.nl/DIPimage/docs/FIP2.3.pdf 

952 

953 Examples 

954 -------- 

955 >>> from scipy import ndimage 

956 >>> import numpy as np 

957 >>> input = np.zeros((8, 8), dtype=int) 

958 >>> input[2, 2] = 1 

959 >>> mask = np.zeros((8, 8), dtype=int) 

960 >>> mask[1:4, 1:4] = mask[4, 4] = mask[6:8, 6:8] = 1 

961 >>> input 

962 array([[0, 0, 0, 0, 0, 0, 0, 0], 

963 [0, 0, 0, 0, 0, 0, 0, 0], 

964 [0, 0, 1, 0, 0, 0, 0, 0], 

965 [0, 0, 0, 0, 0, 0, 0, 0], 

966 [0, 0, 0, 0, 0, 0, 0, 0], 

967 [0, 0, 0, 0, 0, 0, 0, 0], 

968 [0, 0, 0, 0, 0, 0, 0, 0], 

969 [0, 0, 0, 0, 0, 0, 0, 0]]) 

970 >>> mask 

971 array([[0, 0, 0, 0, 0, 0, 0, 0], 

972 [0, 1, 1, 1, 0, 0, 0, 0], 

973 [0, 1, 1, 1, 0, 0, 0, 0], 

974 [0, 1, 1, 1, 0, 0, 0, 0], 

975 [0, 0, 0, 0, 1, 0, 0, 0], 

976 [0, 0, 0, 0, 0, 0, 0, 0], 

977 [0, 0, 0, 0, 0, 0, 1, 1], 

978 [0, 0, 0, 0, 0, 0, 1, 1]]) 

979 >>> ndimage.binary_propagation(input, mask=mask).astype(int) 

980 array([[0, 0, 0, 0, 0, 0, 0, 0], 

981 [0, 1, 1, 1, 0, 0, 0, 0], 

982 [0, 1, 1, 1, 0, 0, 0, 0], 

983 [0, 1, 1, 1, 0, 0, 0, 0], 

984 [0, 0, 0, 0, 0, 0, 0, 0], 

985 [0, 0, 0, 0, 0, 0, 0, 0], 

986 [0, 0, 0, 0, 0, 0, 0, 0], 

987 [0, 0, 0, 0, 0, 0, 0, 0]]) 

988 >>> ndimage.binary_propagation(input, mask=mask,\\ 

989 ... structure=np.ones((3,3))).astype(int) 

990 array([[0, 0, 0, 0, 0, 0, 0, 0], 

991 [0, 1, 1, 1, 0, 0, 0, 0], 

992 [0, 1, 1, 1, 0, 0, 0, 0], 

993 [0, 1, 1, 1, 0, 0, 0, 0], 

994 [0, 0, 0, 0, 1, 0, 0, 0], 

995 [0, 0, 0, 0, 0, 0, 0, 0], 

996 [0, 0, 0, 0, 0, 0, 0, 0], 

997 [0, 0, 0, 0, 0, 0, 0, 0]]) 

998 

999 >>> # Comparison between opening and erosion+propagation 

1000 >>> a = np.zeros((6,6), dtype=int) 

1001 >>> a[2:5, 2:5] = 1; a[0, 0] = 1; a[5, 5] = 1 

1002 >>> a 

1003 array([[1, 0, 0, 0, 0, 0], 

1004 [0, 0, 0, 0, 0, 0], 

1005 [0, 0, 1, 1, 1, 0], 

1006 [0, 0, 1, 1, 1, 0], 

1007 [0, 0, 1, 1, 1, 0], 

1008 [0, 0, 0, 0, 0, 1]]) 

1009 >>> ndimage.binary_opening(a).astype(int) 

1010 array([[0, 0, 0, 0, 0, 0], 

1011 [0, 0, 0, 0, 0, 0], 

1012 [0, 0, 0, 1, 0, 0], 

1013 [0, 0, 1, 1, 1, 0], 

1014 [0, 0, 0, 1, 0, 0], 

1015 [0, 0, 0, 0, 0, 0]]) 

1016 >>> b = ndimage.binary_erosion(a) 

1017 >>> b.astype(int) 

1018 array([[0, 0, 0, 0, 0, 0], 

1019 [0, 0, 0, 0, 0, 0], 

1020 [0, 0, 0, 0, 0, 0], 

1021 [0, 0, 0, 1, 0, 0], 

1022 [0, 0, 0, 0, 0, 0], 

1023 [0, 0, 0, 0, 0, 0]]) 

1024 >>> ndimage.binary_propagation(b, mask=a).astype(int) 

1025 array([[0, 0, 0, 0, 0, 0], 

1026 [0, 0, 0, 0, 0, 0], 

1027 [0, 0, 1, 1, 1, 0], 

1028 [0, 0, 1, 1, 1, 0], 

1029 [0, 0, 1, 1, 1, 0], 

1030 [0, 0, 0, 0, 0, 0]]) 

1031 

1032 """ 

1033 return binary_dilation(input, structure, -1, mask, output, 

1034 border_value, origin) 

1035 

1036 

1037def binary_fill_holes(input, structure=None, output=None, origin=0): 

1038 """ 

1039 Fill the holes in binary objects. 

1040 

1041 

1042 Parameters 

1043 ---------- 

1044 input : array_like 

1045 N-D binary array with holes to be filled 

1046 structure : array_like, optional 

1047 Structuring element used in the computation; large-size elements 

1048 make computations faster but may miss holes separated from the 

1049 background by thin regions. The default element (with a square 

1050 connectivity equal to one) yields the intuitive result where all 

1051 holes in the input have been filled. 

1052 output : ndarray, optional 

1053 Array of the same shape as input, into which the output is placed. 

1054 By default, a new array is created. 

1055 origin : int, tuple of ints, optional 

1056 Position of the structuring element. 

1057 

1058 Returns 

1059 ------- 

1060 out : ndarray 

1061 Transformation of the initial image `input` where holes have been 

1062 filled. 

1063 

1064 See also 

1065 -------- 

1066 binary_dilation, binary_propagation, label 

1067 

1068 Notes 

1069 ----- 

1070 The algorithm used in this function consists in invading the complementary 

1071 of the shapes in `input` from the outer boundary of the image, 

1072 using binary dilations. Holes are not connected to the boundary and are 

1073 therefore not invaded. The result is the complementary subset of the 

1074 invaded region. 

1075 

1076 References 

1077 ---------- 

1078 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology 

1079 

1080 

1081 Examples 

1082 -------- 

1083 >>> from scipy import ndimage 

1084 >>> import numpy as np 

1085 >>> a = np.zeros((5, 5), dtype=int) 

1086 >>> a[1:4, 1:4] = 1 

1087 >>> a[2,2] = 0 

1088 >>> a 

1089 array([[0, 0, 0, 0, 0], 

1090 [0, 1, 1, 1, 0], 

1091 [0, 1, 0, 1, 0], 

1092 [0, 1, 1, 1, 0], 

1093 [0, 0, 0, 0, 0]]) 

1094 >>> ndimage.binary_fill_holes(a).astype(int) 

1095 array([[0, 0, 0, 0, 0], 

1096 [0, 1, 1, 1, 0], 

1097 [0, 1, 1, 1, 0], 

1098 [0, 1, 1, 1, 0], 

1099 [0, 0, 0, 0, 0]]) 

1100 >>> # Too big structuring element 

1101 >>> ndimage.binary_fill_holes(a, structure=np.ones((5,5))).astype(int) 

1102 array([[0, 0, 0, 0, 0], 

1103 [0, 1, 1, 1, 0], 

1104 [0, 1, 0, 1, 0], 

1105 [0, 1, 1, 1, 0], 

1106 [0, 0, 0, 0, 0]]) 

1107 

1108 """ 

1109 mask = numpy.logical_not(input) 

1110 tmp = numpy.zeros(mask.shape, bool) 

1111 inplace = isinstance(output, numpy.ndarray) 

1112 if inplace: 

1113 binary_dilation(tmp, structure, -1, mask, output, 1, origin) 

1114 numpy.logical_not(output, output) 

1115 else: 

1116 output = binary_dilation(tmp, structure, -1, mask, None, 1, 

1117 origin) 

1118 numpy.logical_not(output, output) 

1119 return output 

1120 

1121 

1122def grey_erosion(input, size=None, footprint=None, structure=None, 

1123 output=None, mode="reflect", cval=0.0, origin=0): 

1124 """ 

1125 Calculate a greyscale erosion, using either a structuring element, 

1126 or a footprint corresponding to a flat structuring element. 

1127 

1128 Grayscale erosion is a mathematical morphology operation. For the 

1129 simple case of a full and flat structuring element, it can be viewed 

1130 as a minimum filter over a sliding window. 

1131 

1132 Parameters 

1133 ---------- 

1134 input : array_like 

1135 Array over which the grayscale erosion is to be computed. 

1136 size : tuple of ints 

1137 Shape of a flat and full structuring element used for the grayscale 

1138 erosion. Optional if `footprint` or `structure` is provided. 

1139 footprint : array of ints, optional 

1140 Positions of non-infinite elements of a flat structuring element 

1141 used for the grayscale erosion. Non-zero values give the set of 

1142 neighbors of the center over which the minimum is chosen. 

1143 structure : array of ints, optional 

1144 Structuring element used for the grayscale erosion. `structure` 

1145 may be a non-flat structuring element. 

1146 output : array, optional 

1147 An array used for storing the output of the erosion may be provided. 

1148 mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional 

1149 The `mode` parameter determines how the array borders are 

1150 handled, where `cval` is the value when mode is equal to 

1151 'constant'. Default is 'reflect' 

1152 cval : scalar, optional 

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

1154 is 0.0. 

1155 origin : scalar, optional 

1156 The `origin` parameter controls the placement of the filter. 

1157 Default 0 

1158 

1159 Returns 

1160 ------- 

1161 output : ndarray 

1162 Grayscale erosion of `input`. 

1163 

1164 See also 

1165 -------- 

1166 binary_erosion, grey_dilation, grey_opening, grey_closing 

1167 generate_binary_structure, minimum_filter 

1168 

1169 Notes 

1170 ----- 

1171 The grayscale erosion of an image input by a structuring element s defined 

1172 over a domain E is given by: 

1173 

1174 (input+s)(x) = min {input(y) - s(x-y), for y in E} 

1175 

1176 In particular, for structuring elements defined as 

1177 s(y) = 0 for y in E, the grayscale erosion computes the minimum of the 

1178 input image inside a sliding window defined by E. 

1179 

1180 Grayscale erosion [1]_ is a *mathematical morphology* operation [2]_. 

1181 

1182 References 

1183 ---------- 

1184 .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29 

1185 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology 

1186 

1187 Examples 

1188 -------- 

1189 >>> from scipy import ndimage 

1190 >>> import numpy as np 

1191 >>> a = np.zeros((7,7), dtype=int) 

1192 >>> a[1:6, 1:6] = 3 

1193 >>> a[4,4] = 2; a[2,3] = 1 

1194 >>> a 

1195 array([[0, 0, 0, 0, 0, 0, 0], 

1196 [0, 3, 3, 3, 3, 3, 0], 

1197 [0, 3, 3, 1, 3, 3, 0], 

1198 [0, 3, 3, 3, 3, 3, 0], 

1199 [0, 3, 3, 3, 2, 3, 0], 

1200 [0, 3, 3, 3, 3, 3, 0], 

1201 [0, 0, 0, 0, 0, 0, 0]]) 

1202 >>> ndimage.grey_erosion(a, size=(3,3)) 

1203 array([[0, 0, 0, 0, 0, 0, 0], 

1204 [0, 0, 0, 0, 0, 0, 0], 

1205 [0, 0, 1, 1, 1, 0, 0], 

1206 [0, 0, 1, 1, 1, 0, 0], 

1207 [0, 0, 3, 2, 2, 0, 0], 

1208 [0, 0, 0, 0, 0, 0, 0], 

1209 [0, 0, 0, 0, 0, 0, 0]]) 

1210 >>> footprint = ndimage.generate_binary_structure(2, 1) 

1211 >>> footprint 

1212 array([[False, True, False], 

1213 [ True, True, True], 

1214 [False, True, False]], dtype=bool) 

1215 >>> # Diagonally-connected elements are not considered neighbors 

1216 >>> ndimage.grey_erosion(a, size=(3,3), footprint=footprint) 

1217 array([[0, 0, 0, 0, 0, 0, 0], 

1218 [0, 0, 0, 0, 0, 0, 0], 

1219 [0, 0, 1, 1, 1, 0, 0], 

1220 [0, 0, 3, 1, 2, 0, 0], 

1221 [0, 0, 3, 2, 2, 0, 0], 

1222 [0, 0, 0, 0, 0, 0, 0], 

1223 [0, 0, 0, 0, 0, 0, 0]]) 

1224 

1225 """ 

1226 if size is None and footprint is None and structure is None: 

1227 raise ValueError("size, footprint, or structure must be specified") 

1228 

1229 return _filters._min_or_max_filter(input, size, footprint, structure, 

1230 output, mode, cval, origin, 1) 

1231 

1232 

1233def grey_dilation(input, size=None, footprint=None, structure=None, 

1234 output=None, mode="reflect", cval=0.0, origin=0): 

1235 """ 

1236 Calculate a greyscale dilation, using either a structuring element, 

1237 or a footprint corresponding to a flat structuring element. 

1238 

1239 Grayscale dilation is a mathematical morphology operation. For the 

1240 simple case of a full and flat structuring element, it can be viewed 

1241 as a maximum filter over a sliding window. 

1242 

1243 Parameters 

1244 ---------- 

1245 input : array_like 

1246 Array over which the grayscale dilation is to be computed. 

1247 size : tuple of ints 

1248 Shape of a flat and full structuring element used for the grayscale 

1249 dilation. Optional if `footprint` or `structure` is provided. 

1250 footprint : array of ints, optional 

1251 Positions of non-infinite elements of a flat structuring element 

1252 used for the grayscale dilation. Non-zero values give the set of 

1253 neighbors of the center over which the maximum is chosen. 

1254 structure : array of ints, optional 

1255 Structuring element used for the grayscale dilation. `structure` 

1256 may be a non-flat structuring element. 

1257 output : array, optional 

1258 An array used for storing the output of the dilation may be provided. 

1259 mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional 

1260 The `mode` parameter determines how the array borders are 

1261 handled, where `cval` is the value when mode is equal to 

1262 'constant'. Default is 'reflect' 

1263 cval : scalar, optional 

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

1265 is 0.0. 

1266 origin : scalar, optional 

1267 The `origin` parameter controls the placement of the filter. 

1268 Default 0 

1269 

1270 Returns 

1271 ------- 

1272 grey_dilation : ndarray 

1273 Grayscale dilation of `input`. 

1274 

1275 See also 

1276 -------- 

1277 binary_dilation, grey_erosion, grey_closing, grey_opening 

1278 generate_binary_structure, maximum_filter 

1279 

1280 Notes 

1281 ----- 

1282 The grayscale dilation of an image input by a structuring element s defined 

1283 over a domain E is given by: 

1284 

1285 (input+s)(x) = max {input(y) + s(x-y), for y in E} 

1286 

1287 In particular, for structuring elements defined as 

1288 s(y) = 0 for y in E, the grayscale dilation computes the maximum of the 

1289 input image inside a sliding window defined by E. 

1290 

1291 Grayscale dilation [1]_ is a *mathematical morphology* operation [2]_. 

1292 

1293 References 

1294 ---------- 

1295 .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29 

1296 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology 

1297 

1298 Examples 

1299 -------- 

1300 >>> from scipy import ndimage 

1301 >>> import numpy as np 

1302 >>> a = np.zeros((7,7), dtype=int) 

1303 >>> a[2:5, 2:5] = 1 

1304 >>> a[4,4] = 2; a[2,3] = 3 

1305 >>> a 

1306 array([[0, 0, 0, 0, 0, 0, 0], 

1307 [0, 0, 0, 0, 0, 0, 0], 

1308 [0, 0, 1, 3, 1, 0, 0], 

1309 [0, 0, 1, 1, 1, 0, 0], 

1310 [0, 0, 1, 1, 2, 0, 0], 

1311 [0, 0, 0, 0, 0, 0, 0], 

1312 [0, 0, 0, 0, 0, 0, 0]]) 

1313 >>> ndimage.grey_dilation(a, size=(3,3)) 

1314 array([[0, 0, 0, 0, 0, 0, 0], 

1315 [0, 1, 3, 3, 3, 1, 0], 

1316 [0, 1, 3, 3, 3, 1, 0], 

1317 [0, 1, 3, 3, 3, 2, 0], 

1318 [0, 1, 1, 2, 2, 2, 0], 

1319 [0, 1, 1, 2, 2, 2, 0], 

1320 [0, 0, 0, 0, 0, 0, 0]]) 

1321 >>> ndimage.grey_dilation(a, footprint=np.ones((3,3))) 

1322 array([[0, 0, 0, 0, 0, 0, 0], 

1323 [0, 1, 3, 3, 3, 1, 0], 

1324 [0, 1, 3, 3, 3, 1, 0], 

1325 [0, 1, 3, 3, 3, 2, 0], 

1326 [0, 1, 1, 2, 2, 2, 0], 

1327 [0, 1, 1, 2, 2, 2, 0], 

1328 [0, 0, 0, 0, 0, 0, 0]]) 

1329 >>> s = ndimage.generate_binary_structure(2,1) 

1330 >>> s 

1331 array([[False, True, False], 

1332 [ True, True, True], 

1333 [False, True, False]], dtype=bool) 

1334 >>> ndimage.grey_dilation(a, footprint=s) 

1335 array([[0, 0, 0, 0, 0, 0, 0], 

1336 [0, 0, 1, 3, 1, 0, 0], 

1337 [0, 1, 3, 3, 3, 1, 0], 

1338 [0, 1, 1, 3, 2, 1, 0], 

1339 [0, 1, 1, 2, 2, 2, 0], 

1340 [0, 0, 1, 1, 2, 0, 0], 

1341 [0, 0, 0, 0, 0, 0, 0]]) 

1342 >>> ndimage.grey_dilation(a, size=(3,3), structure=np.ones((3,3))) 

1343 array([[1, 1, 1, 1, 1, 1, 1], 

1344 [1, 2, 4, 4, 4, 2, 1], 

1345 [1, 2, 4, 4, 4, 2, 1], 

1346 [1, 2, 4, 4, 4, 3, 1], 

1347 [1, 2, 2, 3, 3, 3, 1], 

1348 [1, 2, 2, 3, 3, 3, 1], 

1349 [1, 1, 1, 1, 1, 1, 1]]) 

1350 

1351 """ 

1352 if size is None and footprint is None and structure is None: 

1353 raise ValueError("size, footprint, or structure must be specified") 

1354 if structure is not None: 

1355 structure = numpy.asarray(structure) 

1356 structure = structure[tuple([slice(None, None, -1)] * 

1357 structure.ndim)] 

1358 if footprint is not None: 

1359 footprint = numpy.asarray(footprint) 

1360 footprint = footprint[tuple([slice(None, None, -1)] * 

1361 footprint.ndim)] 

1362 

1363 input = numpy.asarray(input) 

1364 origin = _ni_support._normalize_sequence(origin, input.ndim) 

1365 for ii in range(len(origin)): 

1366 origin[ii] = -origin[ii] 

1367 if footprint is not None: 

1368 sz = footprint.shape[ii] 

1369 elif structure is not None: 

1370 sz = structure.shape[ii] 

1371 elif numpy.isscalar(size): 

1372 sz = size 

1373 else: 

1374 sz = size[ii] 

1375 if not sz & 1: 

1376 origin[ii] -= 1 

1377 

1378 return _filters._min_or_max_filter(input, size, footprint, structure, 

1379 output, mode, cval, origin, 0) 

1380 

1381 

1382def grey_opening(input, size=None, footprint=None, structure=None, 

1383 output=None, mode="reflect", cval=0.0, origin=0): 

1384 """ 

1385 Multidimensional grayscale opening. 

1386 

1387 A grayscale opening consists in the succession of a grayscale erosion, 

1388 and a grayscale dilation. 

1389 

1390 Parameters 

1391 ---------- 

1392 input : array_like 

1393 Array over which the grayscale opening is to be computed. 

1394 size : tuple of ints 

1395 Shape of a flat and full structuring element used for the grayscale 

1396 opening. Optional if `footprint` or `structure` is provided. 

1397 footprint : array of ints, optional 

1398 Positions of non-infinite elements of a flat structuring element 

1399 used for the grayscale opening. 

1400 structure : array of ints, optional 

1401 Structuring element used for the grayscale opening. `structure` 

1402 may be a non-flat structuring element. 

1403 output : array, optional 

1404 An array used for storing the output of the opening may be provided. 

1405 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional 

1406 The `mode` parameter determines how the array borders are 

1407 handled, where `cval` is the value when mode is equal to 

1408 'constant'. Default is 'reflect' 

1409 cval : scalar, optional 

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

1411 is 0.0. 

1412 origin : scalar, optional 

1413 The `origin` parameter controls the placement of the filter. 

1414 Default 0 

1415 

1416 Returns 

1417 ------- 

1418 grey_opening : ndarray 

1419 Result of the grayscale opening of `input` with `structure`. 

1420 

1421 See also 

1422 -------- 

1423 binary_opening, grey_dilation, grey_erosion, grey_closing 

1424 generate_binary_structure 

1425 

1426 Notes 

1427 ----- 

1428 The action of a grayscale opening with a flat structuring element amounts 

1429 to smoothen high local maxima, whereas binary opening erases small objects. 

1430 

1431 References 

1432 ---------- 

1433 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology 

1434 

1435 Examples 

1436 -------- 

1437 >>> from scipy import ndimage 

1438 >>> import numpy as np 

1439 >>> a = np.arange(36).reshape((6,6)) 

1440 >>> a[3, 3] = 50 

1441 >>> a 

1442 array([[ 0, 1, 2, 3, 4, 5], 

1443 [ 6, 7, 8, 9, 10, 11], 

1444 [12, 13, 14, 15, 16, 17], 

1445 [18, 19, 20, 50, 22, 23], 

1446 [24, 25, 26, 27, 28, 29], 

1447 [30, 31, 32, 33, 34, 35]]) 

1448 >>> ndimage.grey_opening(a, size=(3,3)) 

1449 array([[ 0, 1, 2, 3, 4, 4], 

1450 [ 6, 7, 8, 9, 10, 10], 

1451 [12, 13, 14, 15, 16, 16], 

1452 [18, 19, 20, 22, 22, 22], 

1453 [24, 25, 26, 27, 28, 28], 

1454 [24, 25, 26, 27, 28, 28]]) 

1455 >>> # Note that the local maximum a[3,3] has disappeared 

1456 

1457 """ 

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

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

1460 tmp = grey_erosion(input, size, footprint, structure, None, mode, 

1461 cval, origin) 

1462 return grey_dilation(tmp, size, footprint, structure, output, mode, 

1463 cval, origin) 

1464 

1465 

1466def grey_closing(input, size=None, footprint=None, structure=None, 

1467 output=None, mode="reflect", cval=0.0, origin=0): 

1468 """ 

1469 Multidimensional grayscale closing. 

1470 

1471 A grayscale closing consists in the succession of a grayscale dilation, 

1472 and a grayscale erosion. 

1473 

1474 Parameters 

1475 ---------- 

1476 input : array_like 

1477 Array over which the grayscale closing is to be computed. 

1478 size : tuple of ints 

1479 Shape of a flat and full structuring element used for the grayscale 

1480 closing. Optional if `footprint` or `structure` is provided. 

1481 footprint : array of ints, optional 

1482 Positions of non-infinite elements of a flat structuring element 

1483 used for the grayscale closing. 

1484 structure : array of ints, optional 

1485 Structuring element used for the grayscale closing. `structure` 

1486 may be a non-flat structuring element. 

1487 output : array, optional 

1488 An array used for storing the output of the closing may be provided. 

1489 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional 

1490 The `mode` parameter determines how the array borders are 

1491 handled, where `cval` is the value when mode is equal to 

1492 'constant'. Default is 'reflect' 

1493 cval : scalar, optional 

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

1495 is 0.0. 

1496 origin : scalar, optional 

1497 The `origin` parameter controls the placement of the filter. 

1498 Default 0 

1499 

1500 Returns 

1501 ------- 

1502 grey_closing : ndarray 

1503 Result of the grayscale closing of `input` with `structure`. 

1504 

1505 See also 

1506 -------- 

1507 binary_closing, grey_dilation, grey_erosion, grey_opening, 

1508 generate_binary_structure 

1509 

1510 Notes 

1511 ----- 

1512 The action of a grayscale closing with a flat structuring element amounts 

1513 to smoothen deep local minima, whereas binary closing fills small holes. 

1514 

1515 References 

1516 ---------- 

1517 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology 

1518 

1519 Examples 

1520 -------- 

1521 >>> from scipy import ndimage 

1522 >>> import numpy as np 

1523 >>> a = np.arange(36).reshape((6,6)) 

1524 >>> a[3,3] = 0 

1525 >>> a 

1526 array([[ 0, 1, 2, 3, 4, 5], 

1527 [ 6, 7, 8, 9, 10, 11], 

1528 [12, 13, 14, 15, 16, 17], 

1529 [18, 19, 20, 0, 22, 23], 

1530 [24, 25, 26, 27, 28, 29], 

1531 [30, 31, 32, 33, 34, 35]]) 

1532 >>> ndimage.grey_closing(a, size=(3,3)) 

1533 array([[ 7, 7, 8, 9, 10, 11], 

1534 [ 7, 7, 8, 9, 10, 11], 

1535 [13, 13, 14, 15, 16, 17], 

1536 [19, 19, 20, 20, 22, 23], 

1537 [25, 25, 26, 27, 28, 29], 

1538 [31, 31, 32, 33, 34, 35]]) 

1539 >>> # Note that the local minimum a[3,3] has disappeared 

1540 

1541 """ 

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

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

1544 tmp = grey_dilation(input, size, footprint, structure, None, mode, 

1545 cval, origin) 

1546 return grey_erosion(tmp, size, footprint, structure, output, mode, 

1547 cval, origin) 

1548 

1549 

1550def morphological_gradient(input, size=None, footprint=None, structure=None, 

1551 output=None, mode="reflect", cval=0.0, origin=0): 

1552 """ 

1553 Multidimensional morphological gradient. 

1554 

1555 The morphological gradient is calculated as the difference between a 

1556 dilation and an erosion of the input with a given structuring element. 

1557 

1558 Parameters 

1559 ---------- 

1560 input : array_like 

1561 Array over which to compute the morphlogical gradient. 

1562 size : tuple of ints 

1563 Shape of a flat and full structuring element used for the mathematical 

1564 morphology operations. Optional if `footprint` or `structure` is 

1565 provided. A larger `size` yields a more blurred gradient. 

1566 footprint : array of ints, optional 

1567 Positions of non-infinite elements of a flat structuring element 

1568 used for the morphology operations. Larger footprints 

1569 give a more blurred morphological gradient. 

1570 structure : array of ints, optional 

1571 Structuring element used for the morphology operations. 

1572 `structure` may be a non-flat structuring element. 

1573 output : array, optional 

1574 An array used for storing the output of the morphological gradient 

1575 may be provided. 

1576 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional 

1577 The `mode` parameter determines how the array borders are 

1578 handled, where `cval` is the value when mode is equal to 

1579 'constant'. Default is 'reflect' 

1580 cval : scalar, optional 

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

1582 is 0.0. 

1583 origin : scalar, optional 

1584 The `origin` parameter controls the placement of the filter. 

1585 Default 0 

1586 

1587 Returns 

1588 ------- 

1589 morphological_gradient : ndarray 

1590 Morphological gradient of `input`. 

1591 

1592 See also 

1593 -------- 

1594 grey_dilation, grey_erosion, gaussian_gradient_magnitude 

1595 

1596 Notes 

1597 ----- 

1598 For a flat structuring element, the morphological gradient 

1599 computed at a given point corresponds to the maximal difference 

1600 between elements of the input among the elements covered by the 

1601 structuring element centered on the point. 

1602 

1603 References 

1604 ---------- 

1605 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology 

1606 

1607 Examples 

1608 -------- 

1609 >>> from scipy import ndimage 

1610 >>> import numpy as np 

1611 >>> a = np.zeros((7,7), dtype=int) 

1612 >>> a[2:5, 2:5] = 1 

1613 >>> ndimage.morphological_gradient(a, size=(3,3)) 

1614 array([[0, 0, 0, 0, 0, 0, 0], 

1615 [0, 1, 1, 1, 1, 1, 0], 

1616 [0, 1, 1, 1, 1, 1, 0], 

1617 [0, 1, 1, 0, 1, 1, 0], 

1618 [0, 1, 1, 1, 1, 1, 0], 

1619 [0, 1, 1, 1, 1, 1, 0], 

1620 [0, 0, 0, 0, 0, 0, 0]]) 

1621 >>> # The morphological gradient is computed as the difference 

1622 >>> # between a dilation and an erosion 

1623 >>> ndimage.grey_dilation(a, size=(3,3)) -\\ 

1624 ... ndimage.grey_erosion(a, size=(3,3)) 

1625 array([[0, 0, 0, 0, 0, 0, 0], 

1626 [0, 1, 1, 1, 1, 1, 0], 

1627 [0, 1, 1, 1, 1, 1, 0], 

1628 [0, 1, 1, 0, 1, 1, 0], 

1629 [0, 1, 1, 1, 1, 1, 0], 

1630 [0, 1, 1, 1, 1, 1, 0], 

1631 [0, 0, 0, 0, 0, 0, 0]]) 

1632 >>> a = np.zeros((7,7), dtype=int) 

1633 >>> a[2:5, 2:5] = 1 

1634 >>> a[4,4] = 2; a[2,3] = 3 

1635 >>> a 

1636 array([[0, 0, 0, 0, 0, 0, 0], 

1637 [0, 0, 0, 0, 0, 0, 0], 

1638 [0, 0, 1, 3, 1, 0, 0], 

1639 [0, 0, 1, 1, 1, 0, 0], 

1640 [0, 0, 1, 1, 2, 0, 0], 

1641 [0, 0, 0, 0, 0, 0, 0], 

1642 [0, 0, 0, 0, 0, 0, 0]]) 

1643 >>> ndimage.morphological_gradient(a, size=(3,3)) 

1644 array([[0, 0, 0, 0, 0, 0, 0], 

1645 [0, 1, 3, 3, 3, 1, 0], 

1646 [0, 1, 3, 3, 3, 1, 0], 

1647 [0, 1, 3, 2, 3, 2, 0], 

1648 [0, 1, 1, 2, 2, 2, 0], 

1649 [0, 1, 1, 2, 2, 2, 0], 

1650 [0, 0, 0, 0, 0, 0, 0]]) 

1651 

1652 """ 

1653 tmp = grey_dilation(input, size, footprint, structure, None, mode, 

1654 cval, origin) 

1655 if isinstance(output, numpy.ndarray): 

1656 grey_erosion(input, size, footprint, structure, output, mode, 

1657 cval, origin) 

1658 return numpy.subtract(tmp, output, output) 

1659 else: 

1660 return (tmp - grey_erosion(input, size, footprint, structure, 

1661 None, mode, cval, origin)) 

1662 

1663 

1664def morphological_laplace(input, size=None, footprint=None, 

1665 structure=None, output=None, 

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

1667 """ 

1668 Multidimensional morphological laplace. 

1669 

1670 Parameters 

1671 ---------- 

1672 input : array_like 

1673 Input. 

1674 size : int or sequence of ints, optional 

1675 See `structure`. 

1676 footprint : bool or ndarray, optional 

1677 See `structure`. 

1678 structure : structure, optional 

1679 Either `size`, `footprint`, or the `structure` must be provided. 

1680 output : ndarray, optional 

1681 An output array can optionally be provided. 

1682 mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional 

1683 The mode parameter determines how the array borders are handled. 

1684 For 'constant' mode, values beyond borders are set to be `cval`. 

1685 Default is 'reflect'. 

1686 cval : scalar, optional 

1687 Value to fill past edges of input if mode is 'constant'. 

1688 Default is 0.0 

1689 origin : origin, optional 

1690 The origin parameter controls the placement of the filter. 

1691 

1692 Returns 

1693 ------- 

1694 morphological_laplace : ndarray 

1695 Output 

1696 

1697 """ 

1698 tmp1 = grey_dilation(input, size, footprint, structure, None, mode, 

1699 cval, origin) 

1700 if isinstance(output, numpy.ndarray): 

1701 grey_erosion(input, size, footprint, structure, output, mode, 

1702 cval, origin) 

1703 numpy.add(tmp1, output, output) 

1704 numpy.subtract(output, input, output) 

1705 return numpy.subtract(output, input, output) 

1706 else: 

1707 tmp2 = grey_erosion(input, size, footprint, structure, None, mode, 

1708 cval, origin) 

1709 numpy.add(tmp1, tmp2, tmp2) 

1710 numpy.subtract(tmp2, input, tmp2) 

1711 numpy.subtract(tmp2, input, tmp2) 

1712 return tmp2 

1713 

1714 

1715def white_tophat(input, size=None, footprint=None, structure=None, 

1716 output=None, mode="reflect", cval=0.0, origin=0): 

1717 """ 

1718 Multidimensional white tophat filter. 

1719 

1720 Parameters 

1721 ---------- 

1722 input : array_like 

1723 Input. 

1724 size : tuple of ints 

1725 Shape of a flat and full structuring element used for the filter. 

1726 Optional if `footprint` or `structure` is provided. 

1727 footprint : array of ints, optional 

1728 Positions of elements of a flat structuring element 

1729 used for the white tophat filter. 

1730 structure : array of ints, optional 

1731 Structuring element used for the filter. `structure` 

1732 may be a non-flat structuring element. 

1733 output : array, optional 

1734 An array used for storing the output of the filter may be provided. 

1735 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional 

1736 The `mode` parameter determines how the array borders are 

1737 handled, where `cval` is the value when mode is equal to 

1738 'constant'. Default is 'reflect' 

1739 cval : scalar, optional 

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

1741 Default is 0.0. 

1742 origin : scalar, optional 

1743 The `origin` parameter controls the placement of the filter. 

1744 Default is 0. 

1745 

1746 Returns 

1747 ------- 

1748 output : ndarray 

1749 Result of the filter of `input` with `structure`. 

1750 

1751 Examples 

1752 -------- 

1753 Subtract gray background from a bright peak. 

1754 

1755 >>> from scipy.ndimage import generate_binary_structure, white_tophat 

1756 >>> import numpy as np 

1757 >>> square = generate_binary_structure(rank=2, connectivity=3) 

1758 >>> bright_on_gray = np.array([[2, 3, 3, 3, 2], 

1759 ... [3, 4, 5, 4, 3], 

1760 ... [3, 5, 9, 5, 3], 

1761 ... [3, 4, 5, 4, 3], 

1762 ... [2, 3, 3, 3, 2]]) 

1763 >>> white_tophat(input=bright_on_gray, structure=square) 

1764 array([[0, 0, 0, 0, 0], 

1765 [0, 0, 1, 0, 0], 

1766 [0, 1, 5, 1, 0], 

1767 [0, 0, 1, 0, 0], 

1768 [0, 0, 0, 0, 0]]) 

1769 

1770 See also 

1771 -------- 

1772 black_tophat 

1773 

1774 """ 

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

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

1777 tmp = grey_erosion(input, size, footprint, structure, None, mode, 

1778 cval, origin) 

1779 tmp = grey_dilation(tmp, size, footprint, structure, output, mode, 

1780 cval, origin) 

1781 if tmp is None: 

1782 tmp = output 

1783 

1784 if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_: 

1785 numpy.bitwise_xor(input, tmp, out=tmp) 

1786 else: 

1787 numpy.subtract(input, tmp, out=tmp) 

1788 return tmp 

1789 

1790 

1791def black_tophat(input, size=None, footprint=None, 

1792 structure=None, output=None, mode="reflect", 

1793 cval=0.0, origin=0): 

1794 """ 

1795 Multidimensional black tophat filter. 

1796 

1797 Parameters 

1798 ---------- 

1799 input : array_like 

1800 Input. 

1801 size : tuple of ints, optional 

1802 Shape of a flat and full structuring element used for the filter. 

1803 Optional if `footprint` or `structure` is provided. 

1804 footprint : array of ints, optional 

1805 Positions of non-infinite elements of a flat structuring element 

1806 used for the black tophat filter. 

1807 structure : array of ints, optional 

1808 Structuring element used for the filter. `structure` 

1809 may be a non-flat structuring element. 

1810 output : array, optional 

1811 An array used for storing the output of the filter may be provided. 

1812 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional 

1813 The `mode` parameter determines how the array borders are 

1814 handled, where `cval` is the value when mode is equal to 

1815 'constant'. Default is 'reflect' 

1816 cval : scalar, optional 

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

1818 is 0.0. 

1819 origin : scalar, optional 

1820 The `origin` parameter controls the placement of the filter. 

1821 Default 0 

1822 

1823 Returns 

1824 ------- 

1825 black_tophat : ndarray 

1826 Result of the filter of `input` with `structure`. 

1827 

1828 Examples 

1829 -------- 

1830 Change dark peak to bright peak and subtract background. 

1831 

1832 >>> from scipy.ndimage import generate_binary_structure, black_tophat 

1833 >>> import numpy as np 

1834 >>> square = generate_binary_structure(rank=2, connectivity=3) 

1835 >>> dark_on_gray = np.array([[7, 6, 6, 6, 7], 

1836 ... [6, 5, 4, 5, 6], 

1837 ... [6, 4, 0, 4, 6], 

1838 ... [6, 5, 4, 5, 6], 

1839 ... [7, 6, 6, 6, 7]]) 

1840 >>> black_tophat(input=dark_on_gray, structure=square) 

1841 array([[0, 0, 0, 0, 0], 

1842 [0, 0, 1, 0, 0], 

1843 [0, 1, 5, 1, 0], 

1844 [0, 0, 1, 0, 0], 

1845 [0, 0, 0, 0, 0]]) 

1846 

1847 See also 

1848 -------- 

1849 white_tophat, grey_opening, grey_closing 

1850 

1851 """ 

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

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

1854 tmp = grey_dilation(input, size, footprint, structure, None, mode, 

1855 cval, origin) 

1856 tmp = grey_erosion(tmp, size, footprint, structure, output, mode, 

1857 cval, origin) 

1858 if tmp is None: 

1859 tmp = output 

1860 

1861 if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_: 

1862 numpy.bitwise_xor(tmp, input, out=tmp) 

1863 else: 

1864 numpy.subtract(tmp, input, out=tmp) 

1865 return tmp 

1866 

1867 

1868def distance_transform_bf(input, metric="euclidean", sampling=None, 

1869 return_distances=True, return_indices=False, 

1870 distances=None, indices=None): 

1871 """ 

1872 Distance transform function by a brute force algorithm. 

1873 

1874 This function calculates the distance transform of the `input`, by 

1875 replacing each foreground (non-zero) element, with its 

1876 shortest distance to the background (any zero-valued element). 

1877 

1878 In addition to the distance transform, the feature transform can 

1879 be calculated. In this case the index of the closest background 

1880 element to each foreground element is returned in a separate array. 

1881 

1882 Parameters 

1883 ---------- 

1884 input : array_like 

1885 Input 

1886 metric : {'euclidean', 'taxicab', 'chessboard'}, optional 

1887 'cityblock' and 'manhattan' are also valid, and map to 'taxicab'. 

1888 The default is 'euclidean'. 

1889 sampling : float, or sequence of float, optional 

1890 This parameter is only used when `metric` is 'euclidean'. 

1891 Spacing of elements along each dimension. If a sequence, must be of 

1892 length equal to the input rank; if a single number, this is used for 

1893 all axes. If not specified, a grid spacing of unity is implied. 

1894 return_distances : bool, optional 

1895 Whether to calculate the distance transform. 

1896 Default is True. 

1897 return_indices : bool, optional 

1898 Whether to calculate the feature transform. 

1899 Default is False. 

1900 distances : ndarray, optional 

1901 An output array to store the calculated distance transform, instead of 

1902 returning it. 

1903 `return_distances` must be True. 

1904 It must be the same shape as `input`, and of type float64 if `metric` 

1905 is 'euclidean', uint32 otherwise. 

1906 indices : int32 ndarray, optional 

1907 An output array to store the calculated feature transform, instead of 

1908 returning it. 

1909 `return_indicies` must be True. 

1910 Its shape must be `(input.ndim,) + input.shape`. 

1911 

1912 Returns 

1913 ------- 

1914 distances : ndarray, optional 

1915 The calculated distance transform. Returned only when 

1916 `return_distances` is True and `distances` is not supplied. 

1917 It will have the same shape as the input array. 

1918 indices : int32 ndarray, optional 

1919 The calculated feature transform. It has an input-shaped array for each 

1920 dimension of the input. See distance_transform_edt documentation for an 

1921 example. 

1922 Returned only when `return_indices` is True and `indices` is not 

1923 supplied. 

1924 

1925 Notes 

1926 ----- 

1927 This function employs a slow brute force algorithm, see also the 

1928 function distance_transform_cdt for more efficient taxicab and 

1929 chessboard algorithms. 

1930 

1931 """ 

1932 ft_inplace = isinstance(indices, numpy.ndarray) 

1933 dt_inplace = isinstance(distances, numpy.ndarray) 

1934 _distance_tranform_arg_check( 

1935 dt_inplace, ft_inplace, return_distances, return_indices 

1936 ) 

1937 

1938 tmp1 = numpy.asarray(input) != 0 

1939 struct = generate_binary_structure(tmp1.ndim, tmp1.ndim) 

1940 tmp2 = binary_dilation(tmp1, struct) 

1941 tmp2 = numpy.logical_xor(tmp1, tmp2) 

1942 tmp1 = tmp1.astype(numpy.int8) - tmp2.astype(numpy.int8) 

1943 metric = metric.lower() 

1944 if metric == 'euclidean': 

1945 metric = 1 

1946 elif metric in ['taxicab', 'cityblock', 'manhattan']: 

1947 metric = 2 

1948 elif metric == 'chessboard': 

1949 metric = 3 

1950 else: 

1951 raise RuntimeError('distance metric not supported') 

1952 if sampling is not None: 

1953 sampling = _ni_support._normalize_sequence(sampling, tmp1.ndim) 

1954 sampling = numpy.asarray(sampling, dtype=numpy.float64) 

1955 if not sampling.flags.contiguous: 

1956 sampling = sampling.copy() 

1957 if return_indices: 

1958 ft = numpy.zeros(tmp1.shape, dtype=numpy.int32) 

1959 else: 

1960 ft = None 

1961 if return_distances: 

1962 if distances is None: 

1963 if metric == 1: 

1964 dt = numpy.zeros(tmp1.shape, dtype=numpy.float64) 

1965 else: 

1966 dt = numpy.zeros(tmp1.shape, dtype=numpy.uint32) 

1967 else: 

1968 if distances.shape != tmp1.shape: 

1969 raise RuntimeError('distances array has wrong shape') 

1970 if metric == 1: 

1971 if distances.dtype.type != numpy.float64: 

1972 raise RuntimeError('distances array must be float64') 

1973 else: 

1974 if distances.dtype.type != numpy.uint32: 

1975 raise RuntimeError('distances array must be uint32') 

1976 dt = distances 

1977 else: 

1978 dt = None 

1979 

1980 _nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft) 

1981 if return_indices: 

1982 if isinstance(indices, numpy.ndarray): 

1983 if indices.dtype.type != numpy.int32: 

1984 raise RuntimeError('indices array must be int32') 

1985 if indices.shape != (tmp1.ndim,) + tmp1.shape: 

1986 raise RuntimeError('indices array has wrong shape') 

1987 tmp2 = indices 

1988 else: 

1989 tmp2 = numpy.indices(tmp1.shape, dtype=numpy.int32) 

1990 ft = numpy.ravel(ft) 

1991 for ii in range(tmp2.shape[0]): 

1992 rtmp = numpy.ravel(tmp2[ii, ...])[ft] 

1993 rtmp.shape = tmp1.shape 

1994 tmp2[ii, ...] = rtmp 

1995 ft = tmp2 

1996 

1997 # construct and return the result 

1998 result = [] 

1999 if return_distances and not dt_inplace: 

2000 result.append(dt) 

2001 if return_indices and not ft_inplace: 

2002 result.append(ft) 

2003 

2004 if len(result) == 2: 

2005 return tuple(result) 

2006 elif len(result) == 1: 

2007 return result[0] 

2008 else: 

2009 return None 

2010 

2011 

2012def distance_transform_cdt(input, metric='chessboard', return_distances=True, 

2013 return_indices=False, distances=None, indices=None): 

2014 """ 

2015 Distance transform for chamfer type of transforms. 

2016 

2017 In addition to the distance transform, the feature transform can 

2018 be calculated. In this case the index of the closest background 

2019 element to each foreground element is returned in a separate array. 

2020 

2021 Parameters 

2022 ---------- 

2023 input : array_like 

2024 Input 

2025 metric : {'chessboard', 'taxicab'} or array_like, optional 

2026 The `metric` determines the type of chamfering that is done. If the 

2027 `metric` is equal to 'taxicab' a structure is generated using 

2028 generate_binary_structure with a squared distance equal to 1. If 

2029 the `metric` is equal to 'chessboard', a `metric` is generated 

2030 using generate_binary_structure with a squared distance equal to 

2031 the dimensionality of the array. These choices correspond to the 

2032 common interpretations of the 'taxicab' and the 'chessboard' 

2033 distance metrics in two dimensions. 

2034 A custom metric may be provided, in the form of a matrix where 

2035 each dimension has a length of three. 

2036 'cityblock' and 'manhattan' are also valid, and map to 'taxicab'. 

2037 The default is 'chessboard'. 

2038 return_distances : bool, optional 

2039 Whether to calculate the distance transform. 

2040 Default is True. 

2041 return_indices : bool, optional 

2042 Whether to calculate the feature transform. 

2043 Default is False. 

2044 distances : int32 ndarray, optional 

2045 An output array to store the calculated distance transform, instead of 

2046 returning it. 

2047 `return_distances` must be True. 

2048 It must be the same shape as `input`. 

2049 indices : int32 ndarray, optional 

2050 An output array to store the calculated feature transform, instead of 

2051 returning it. 

2052 `return_indicies` must be True. 

2053 Its shape must be `(input.ndim,) + input.shape`. 

2054 

2055 Returns 

2056 ------- 

2057 distances : int32 ndarray, optional 

2058 The calculated distance transform. Returned only when 

2059 `return_distances` is True, and `distances` is not supplied. 

2060 It will have the same shape as the input array. 

2061 indices : int32 ndarray, optional 

2062 The calculated feature transform. It has an input-shaped array for each 

2063 dimension of the input. See distance_transform_edt documentation for an 

2064 example. 

2065 Returned only when `return_indices` is True, and `indices` is not 

2066 supplied. 

2067 

2068 """ 

2069 ft_inplace = isinstance(indices, numpy.ndarray) 

2070 dt_inplace = isinstance(distances, numpy.ndarray) 

2071 _distance_tranform_arg_check( 

2072 dt_inplace, ft_inplace, return_distances, return_indices 

2073 ) 

2074 input = numpy.asarray(input) 

2075 if metric in ['taxicab', 'cityblock', 'manhattan']: 

2076 rank = input.ndim 

2077 metric = generate_binary_structure(rank, 1) 

2078 elif metric == 'chessboard': 

2079 rank = input.ndim 

2080 metric = generate_binary_structure(rank, rank) 

2081 else: 

2082 try: 

2083 metric = numpy.asarray(metric) 

2084 except Exception as e: 

2085 raise RuntimeError('invalid metric provided') from e 

2086 for s in metric.shape: 

2087 if s != 3: 

2088 raise RuntimeError('metric sizes must be equal to 3') 

2089 

2090 if not metric.flags.contiguous: 

2091 metric = metric.copy() 

2092 if dt_inplace: 

2093 if distances.dtype.type != numpy.int32: 

2094 raise RuntimeError('distances must be of int32 type') 

2095 if distances.shape != input.shape: 

2096 raise RuntimeError('distances has wrong shape') 

2097 dt = distances 

2098 dt[...] = numpy.where(input, -1, 0).astype(numpy.int32) 

2099 else: 

2100 dt = numpy.where(input, -1, 0).astype(numpy.int32) 

2101 

2102 rank = dt.ndim 

2103 if return_indices: 

2104 sz = numpy.prod(dt.shape, axis=0) 

2105 ft = numpy.arange(sz, dtype=numpy.int32) 

2106 ft.shape = dt.shape 

2107 else: 

2108 ft = None 

2109 

2110 _nd_image.distance_transform_op(metric, dt, ft) 

2111 dt = dt[tuple([slice(None, None, -1)] * rank)] 

2112 if return_indices: 

2113 ft = ft[tuple([slice(None, None, -1)] * rank)] 

2114 _nd_image.distance_transform_op(metric, dt, ft) 

2115 dt = dt[tuple([slice(None, None, -1)] * rank)] 

2116 if return_indices: 

2117 ft = ft[tuple([slice(None, None, -1)] * rank)] 

2118 ft = numpy.ravel(ft) 

2119 if ft_inplace: 

2120 if indices.dtype.type != numpy.int32: 

2121 raise RuntimeError('indices array must be int32') 

2122 if indices.shape != (dt.ndim,) + dt.shape: 

2123 raise RuntimeError('indices array has wrong shape') 

2124 tmp = indices 

2125 else: 

2126 tmp = numpy.indices(dt.shape, dtype=numpy.int32) 

2127 for ii in range(tmp.shape[0]): 

2128 rtmp = numpy.ravel(tmp[ii, ...])[ft] 

2129 rtmp.shape = dt.shape 

2130 tmp[ii, ...] = rtmp 

2131 ft = tmp 

2132 

2133 # construct and return the result 

2134 result = [] 

2135 if return_distances and not dt_inplace: 

2136 result.append(dt) 

2137 if return_indices and not ft_inplace: 

2138 result.append(ft) 

2139 

2140 if len(result) == 2: 

2141 return tuple(result) 

2142 elif len(result) == 1: 

2143 return result[0] 

2144 else: 

2145 return None 

2146 

2147 

2148def distance_transform_edt(input, sampling=None, return_distances=True, 

2149 return_indices=False, distances=None, indices=None): 

2150 """ 

2151 Exact Euclidean distance transform. 

2152 

2153 In addition to the distance transform, the feature transform can 

2154 be calculated. In this case the index of the closest background 

2155 element to each foreground element is returned in a separate array. 

2156 

2157 Parameters 

2158 ---------- 

2159 input : array_like 

2160 Input data to transform. Can be any type but will be converted 

2161 into binary: 1 wherever input equates to True, 0 elsewhere. 

2162 sampling : float, or sequence of float, optional 

2163 Spacing of elements along each dimension. If a sequence, must be of 

2164 length equal to the input rank; if a single number, this is used for 

2165 all axes. If not specified, a grid spacing of unity is implied. 

2166 return_distances : bool, optional 

2167 Whether to calculate the distance transform. 

2168 Default is True. 

2169 return_indices : bool, optional 

2170 Whether to calculate the feature transform. 

2171 Default is False. 

2172 distances : float64 ndarray, optional 

2173 An output array to store the calculated distance transform, instead of 

2174 returning it. 

2175 `return_distances` must be True. 

2176 It must be the same shape as `input`. 

2177 indices : int32 ndarray, optional 

2178 An output array to store the calculated feature transform, instead of 

2179 returning it. 

2180 `return_indicies` must be True. 

2181 Its shape must be `(input.ndim,) + input.shape`. 

2182 

2183 Returns 

2184 ------- 

2185 distances : float64 ndarray, optional 

2186 The calculated distance transform. Returned only when 

2187 `return_distances` is True and `distances` is not supplied. 

2188 It will have the same shape as the input array. 

2189 indices : int32 ndarray, optional 

2190 The calculated feature transform. It has an input-shaped array for each 

2191 dimension of the input. See example below. 

2192 Returned only when `return_indices` is True and `indices` is not 

2193 supplied. 

2194 

2195 Notes 

2196 ----- 

2197 The Euclidean distance transform gives values of the Euclidean 

2198 distance:: 

2199 

2200 n 

2201 y_i = sqrt(sum (x[i]-b[i])**2) 

2202 i 

2203 

2204 where b[i] is the background point (value 0) with the smallest 

2205 Euclidean distance to input points x[i], and n is the 

2206 number of dimensions. 

2207 

2208 Examples 

2209 -------- 

2210 >>> from scipy import ndimage 

2211 >>> import numpy as np 

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

2213 ... [0,0,1,1,1], 

2214 ... [0,1,1,1,1], 

2215 ... [0,1,1,1,0], 

2216 ... [0,1,1,0,0])) 

2217 >>> ndimage.distance_transform_edt(a) 

2218 array([[ 0. , 1. , 1.4142, 2.2361, 3. ], 

2219 [ 0. , 0. , 1. , 2. , 2. ], 

2220 [ 0. , 1. , 1.4142, 1.4142, 1. ], 

2221 [ 0. , 1. , 1.4142, 1. , 0. ], 

2222 [ 0. , 1. , 1. , 0. , 0. ]]) 

2223 

2224 With a sampling of 2 units along x, 1 along y: 

2225 

2226 >>> ndimage.distance_transform_edt(a, sampling=[2,1]) 

2227 array([[ 0. , 1. , 2. , 2.8284, 3.6056], 

2228 [ 0. , 0. , 1. , 2. , 3. ], 

2229 [ 0. , 1. , 2. , 2.2361, 2. ], 

2230 [ 0. , 1. , 2. , 1. , 0. ], 

2231 [ 0. , 1. , 1. , 0. , 0. ]]) 

2232 

2233 Asking for indices as well: 

2234 

2235 >>> edt, inds = ndimage.distance_transform_edt(a, return_indices=True) 

2236 >>> inds 

2237 array([[[0, 0, 1, 1, 3], 

2238 [1, 1, 1, 1, 3], 

2239 [2, 2, 1, 3, 3], 

2240 [3, 3, 4, 4, 3], 

2241 [4, 4, 4, 4, 4]], 

2242 [[0, 0, 1, 1, 4], 

2243 [0, 1, 1, 1, 4], 

2244 [0, 0, 1, 4, 4], 

2245 [0, 0, 3, 3, 4], 

2246 [0, 0, 3, 3, 4]]]) 

2247 

2248 With arrays provided for inplace outputs: 

2249 

2250 >>> indices = np.zeros(((np.ndim(a),) + a.shape), dtype=np.int32) 

2251 >>> ndimage.distance_transform_edt(a, return_indices=True, indices=indices) 

2252 array([[ 0. , 1. , 1.4142, 2.2361, 3. ], 

2253 [ 0. , 0. , 1. , 2. , 2. ], 

2254 [ 0. , 1. , 1.4142, 1.4142, 1. ], 

2255 [ 0. , 1. , 1.4142, 1. , 0. ], 

2256 [ 0. , 1. , 1. , 0. , 0. ]]) 

2257 >>> indices 

2258 array([[[0, 0, 1, 1, 3], 

2259 [1, 1, 1, 1, 3], 

2260 [2, 2, 1, 3, 3], 

2261 [3, 3, 4, 4, 3], 

2262 [4, 4, 4, 4, 4]], 

2263 [[0, 0, 1, 1, 4], 

2264 [0, 1, 1, 1, 4], 

2265 [0, 0, 1, 4, 4], 

2266 [0, 0, 3, 3, 4], 

2267 [0, 0, 3, 3, 4]]]) 

2268 

2269 """ 

2270 ft_inplace = isinstance(indices, numpy.ndarray) 

2271 dt_inplace = isinstance(distances, numpy.ndarray) 

2272 _distance_tranform_arg_check( 

2273 dt_inplace, ft_inplace, return_distances, return_indices 

2274 ) 

2275 

2276 # calculate the feature transform 

2277 input = numpy.atleast_1d(numpy.where(input, 1, 0).astype(numpy.int8)) 

2278 if sampling is not None: 

2279 sampling = _ni_support._normalize_sequence(sampling, input.ndim) 

2280 sampling = numpy.asarray(sampling, dtype=numpy.float64) 

2281 if not sampling.flags.contiguous: 

2282 sampling = sampling.copy() 

2283 

2284 if ft_inplace: 

2285 ft = indices 

2286 if ft.shape != (input.ndim,) + input.shape: 

2287 raise RuntimeError('indices array has wrong shape') 

2288 if ft.dtype.type != numpy.int32: 

2289 raise RuntimeError('indices array must be int32') 

2290 else: 

2291 ft = numpy.zeros((input.ndim,) + input.shape, dtype=numpy.int32) 

2292 

2293 _nd_image.euclidean_feature_transform(input, sampling, ft) 

2294 # if requested, calculate the distance transform 

2295 if return_distances: 

2296 dt = ft - numpy.indices(input.shape, dtype=ft.dtype) 

2297 dt = dt.astype(numpy.float64) 

2298 if sampling is not None: 

2299 for ii in range(len(sampling)): 

2300 dt[ii, ...] *= sampling[ii] 

2301 numpy.multiply(dt, dt, dt) 

2302 if dt_inplace: 

2303 dt = numpy.add.reduce(dt, axis=0) 

2304 if distances.shape != dt.shape: 

2305 raise RuntimeError('distances array has wrong shape') 

2306 if distances.dtype.type != numpy.float64: 

2307 raise RuntimeError('distances array must be float64') 

2308 numpy.sqrt(dt, distances) 

2309 else: 

2310 dt = numpy.add.reduce(dt, axis=0) 

2311 dt = numpy.sqrt(dt) 

2312 

2313 # construct and return the result 

2314 result = [] 

2315 if return_distances and not dt_inplace: 

2316 result.append(dt) 

2317 if return_indices and not ft_inplace: 

2318 result.append(ft) 

2319 

2320 if len(result) == 2: 

2321 return tuple(result) 

2322 elif len(result) == 1: 

2323 return result[0] 

2324 else: 

2325 return None 

2326 

2327 

2328def _distance_tranform_arg_check(distances_out, indices_out, 

2329 return_distances, return_indices): 

2330 """Raise a RuntimeError if the arguments are invalid""" 

2331 error_msgs = [] 

2332 if (not return_distances) and (not return_indices): 

2333 error_msgs.append( 

2334 'at least one of return_distances/return_indices must be True') 

2335 if distances_out and not return_distances: 

2336 error_msgs.append( 

2337 'return_distances must be True if distances is supplied' 

2338 ) 

2339 if indices_out and not return_indices: 

2340 error_msgs.append('return_indices must be True if indices is supplied') 

2341 if error_msgs: 

2342 raise RuntimeError(', '.join(error_msgs))