Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

2# 

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

4# modification, are permitted provided that the following conditions 

5# are met: 

6# 

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

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

9# 

10# 2. Redistributions in binary form must reproduce the above 

11# copyright notice, this list of conditions and the following 

12# disclaimer in the documentation and/or other materials provided 

13# with the distribution. 

14# 

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

16# products derived from this software without specific prior 

17# written permission. 

18# 

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

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

21# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 

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

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

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

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

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

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

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

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

30 

31import 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 >>> struct = ndimage.generate_binary_structure(2, 1) 

163 >>> struct 

164 array([[False, True, False], 

165 [ True, True, True], 

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

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

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

169 >>> a 

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

171 [ 0., 0., 0., 0., 0.], 

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

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

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

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

176 >>> b 

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

178 [ 0., 0., 1., 0., 0.], 

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

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

181 [ 0., 0., 0., 0., 0.]]) 

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

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

184 [ 0., 1., 1., 1., 0.], 

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

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

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

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

189 >>> struct 

190 array([[ True, True, True], 

191 [ True, True, True], 

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

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

194 >>> struct # no diagonal elements 

195 array([[[False, False, False], 

196 [False, True, False], 

197 [False, False, False]], 

198 [[False, True, False], 

199 [ True, True, True], 

200 [False, True, False]], 

201 [[False, False, False], 

202 [False, True, False], 

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

204 

205 """ 

206 if connectivity < 1: 

207 connectivity = 1 

208 if rank < 1: 

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

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

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

212 return output <= connectivity 

213 

214 

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

216 border_value, origin, invert, brute_force): 

217 try: 

218 iterations = operator.index(iterations) 

219 except TypeError: 

220 raise TypeError('iterations parameter should be an integer') 

221 

222 input = numpy.asarray(input) 

223 if numpy.iscomplexobj(input): 

224 raise TypeError('Complex type not supported') 

225 if structure is None: 

226 structure = generate_binary_structure(input.ndim, 1) 

227 else: 

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

229 if structure.ndim != input.ndim: 

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

231 if not structure.flags.contiguous: 

232 structure = structure.copy() 

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

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

235 if mask is not None: 

236 mask = numpy.asarray(mask) 

237 if mask.shape != input.shape: 

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

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

240 cit = _center_is_true(structure, origin) 

241 if isinstance(output, numpy.ndarray): 

242 if numpy.iscomplexobj(output): 

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

244 else: 

245 output = bool 

246 output = _ni_support._get_output(output, input) 

247 temp_needed = numpy.may_share_memory(input, output) 

248 if temp_needed: 

249 # input and output arrays cannot share memory 

250 temp = output 

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

252 if iterations == 1: 

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

254 border_value, origin, invert, cit, 0) 

255 return output 

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 >>> a = np.zeros((7,7), dtype=int) 

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

359 >>> a 

360 array([[0, 0, 0, 0, 0, 0, 0], 

361 [0, 0, 1, 1, 1, 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, 0, 0, 0, 0, 0]]) 

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

368 array([[0, 0, 0, 0, 0, 0, 0], 

369 [0, 0, 0, 0, 0, 0, 0], 

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

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

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

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

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

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

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

377 array([[0, 0, 0, 0, 0, 0, 0], 

378 [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 

385 """ 

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

387 output, border_value, origin, 0, brute_force) 

388 

389 

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

391 output=None, border_value=0, origin=0, 

392 brute_force=False): 

393 """ 

394 Multidimensional binary dilation with the given structuring element. 

395 

396 Parameters 

397 ---------- 

398 input : array_like 

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

400 the subset to be dilated. 

401 structure : array_like, optional 

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

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

404 is generated with a square connectivity equal to one. 

405 iterations : int, optional 

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

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

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

409 accepted. 

410 mask : array_like, optional 

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

412 the corresponding mask element are modified at each iteration. 

413 output : ndarray, optional 

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

415 By default, a new array is created. 

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

417 Value at the border in the output array. 

418 origin : int or tuple of ints, optional 

419 Placement of the filter, by default 0. 

420 brute_force : boolean, optional 

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

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

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

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

425 iteration. False by default. 

426 

427 Returns 

428 ------- 

429 binary_dilation : ndarray of bools 

430 Dilation of the input by the structuring element. 

431 

432 See also 

433 -------- 

434 grey_dilation, binary_erosion, binary_closing, binary_opening, 

435 generate_binary_structure 

436 

437 Notes 

438 ----- 

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

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

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

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

443 non-zero points of the image. 

444 

445 References 

446 ---------- 

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

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

449 

450 Examples 

451 -------- 

452 >>> from scipy import ndimage 

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

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

455 >>> a 

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

457 [ 0., 0., 0., 0., 0.], 

458 [ 0., 0., 1., 0., 0.], 

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

460 [ 0., 0., 0., 0., 0.]]) 

461 >>> ndimage.binary_dilation(a) 

462 array([[False, False, False, False, False], 

463 [False, False, True, False, False], 

464 [False, True, True, True, False], 

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

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

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

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

469 [ 0., 0., 1., 0., 0.], 

470 [ 0., 1., 1., 1., 0.], 

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

472 [ 0., 0., 0., 0., 0.]]) 

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

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

475 >>> struct1 

476 array([[False, True, False], 

477 [ True, True, True], 

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

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

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

481 >>> struct2 

482 array([[ True, True, True], 

483 [ True, True, True], 

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

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

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

487 [ 0., 0., 1., 0., 0.], 

488 [ 0., 1., 1., 1., 0.], 

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

490 [ 0., 0., 0., 0., 0.]]) 

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

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

493 [ 0., 1., 1., 1., 0.], 

494 [ 0., 1., 1., 1., 0.], 

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

496 [ 0., 0., 0., 0., 0.]]) 

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

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

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

500 [ 0., 1., 1., 1., 0.], 

501 [ 1., 1., 1., 1., 1.], 

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

503 [ 0., 0., 1., 0., 0.]]) 

504 

505 """ 

506 input = numpy.asarray(input) 

507 if structure is None: 

508 structure = generate_binary_structure(input.ndim, 1) 

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

510 structure = numpy.asarray(structure) 

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

512 structure.ndim)] 

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

514 origin[ii] = -origin[ii] 

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

516 origin[ii] -= 1 

517 

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

519 output, border_value, origin, 1, brute_force) 

520 

521 

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

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

524 """ 

525 Multidimensional binary opening with the given structuring element. 

526 

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

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

529 

530 Parameters 

531 ---------- 

532 input : array_like 

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

534 the subset to be opened. 

535 structure : array_like, optional 

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

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

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

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

540 elements are not considered neighbors). 

541 iterations : int, optional 

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

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

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

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

546 output : ndarray, optional 

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

548 By default, a new array is created. 

549 origin : int or tuple of ints, optional 

550 Placement of the filter, by default 0. 

551 mask : array_like, optional 

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

553 the corresponding mask element are modified at each iteration. 

554 

555 .. versionadded:: 1.1.0 

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

557 Value at the border in the output array. 

558 

559 .. versionadded:: 1.1.0 

560 brute_force : boolean, optional 

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

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

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

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

565 False by default. 

566 

567 .. versionadded:: 1.1.0 

568 

569 Returns 

570 ------- 

571 binary_opening : ndarray of bools 

572 Opening of the input by the structuring element. 

573 

574 See also 

575 -------- 

576 grey_opening, binary_closing, binary_erosion, binary_dilation, 

577 generate_binary_structure 

578 

579 Notes 

580 ----- 

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

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

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

584 objects smaller than the structuring element. 

585 

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

587 noise removal. 

588 

589 References 

590 ---------- 

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

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

593 

594 Examples 

595 -------- 

596 >>> from scipy import ndimage 

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

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

599 >>> a 

600 array([[0, 0, 0, 0, 0], 

601 [0, 1, 1, 1, 0], 

602 [0, 1, 1, 1, 0], 

603 [0, 1, 1, 1, 0], 

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

605 >>> # Opening removes small objects 

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

607 array([[0, 0, 0, 0, 0], 

608 [0, 1, 1, 1, 0], 

609 [0, 1, 1, 1, 0], 

610 [0, 1, 1, 1, 0], 

611 [0, 0, 0, 0, 0]]) 

612 >>> # Opening can also smooth corners 

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

614 array([[0, 0, 0, 0, 0], 

615 [0, 0, 1, 0, 0], 

616 [0, 1, 1, 1, 0], 

617 [0, 0, 1, 0, 0], 

618 [0, 0, 0, 0, 0]]) 

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

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

621 array([[0, 0, 0, 0, 0], 

622 [0, 0, 0, 0, 0], 

623 [0, 0, 1, 0, 0], 

624 [0, 0, 0, 0, 0], 

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

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

627 array([[0, 0, 0, 0, 0], 

628 [0, 0, 1, 0, 0], 

629 [0, 1, 1, 1, 0], 

630 [0, 0, 1, 0, 0], 

631 [0, 0, 0, 0, 0]]) 

632 

633 """ 

634 input = numpy.asarray(input) 

635 if structure is None: 

636 rank = input.ndim 

637 structure = generate_binary_structure(rank, 1) 

638 

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

640 border_value, origin, brute_force) 

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

642 border_value, origin, brute_force) 

643 

644 

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

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

647 """ 

648 Multidimensional binary closing with the given structuring element. 

649 

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

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

652 

653 Parameters 

654 ---------- 

655 input : array_like 

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

657 the subset to be closed. 

658 structure : array_like, optional 

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

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

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

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

663 elements are not considered neighbors). 

664 iterations : int, optional 

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

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

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

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

669 output : ndarray, optional 

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

671 By default, a new array is created. 

672 origin : int or tuple of ints, optional 

673 Placement of the filter, by default 0. 

674 mask : array_like, optional 

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

676 the corresponding mask element are modified at each iteration. 

677 

678 .. versionadded:: 1.1.0 

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

680 Value at the border in the output array. 

681 

682 .. versionadded:: 1.1.0 

683 brute_force : boolean, optional 

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

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

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

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

688 False by default. 

689 

690 .. versionadded:: 1.1.0 

691 

692 Returns 

693 ------- 

694 binary_closing : ndarray of bools 

695 Closing of the input by the structuring element. 

696 

697 See also 

698 -------- 

699 grey_closing, binary_opening, binary_dilation, binary_erosion, 

700 generate_binary_structure 

701 

702 Notes 

703 ----- 

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

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

706 input with the same structuring element. Closing therefore fills 

707 holes smaller than the structuring element. 

708 

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

710 noise removal. 

711 

712 References 

713 ---------- 

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

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

716 

717 Examples 

718 -------- 

719 >>> from scipy import ndimage 

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

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

722 >>> a 

723 array([[0, 0, 0, 0, 0], 

724 [0, 1, 1, 1, 0], 

725 [0, 1, 0, 1, 0], 

726 [0, 1, 1, 1, 0], 

727 [0, 0, 0, 0, 0]]) 

728 >>> # Closing removes small holes 

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

730 array([[0, 0, 0, 0, 0], 

731 [0, 1, 1, 1, 0], 

732 [0, 1, 1, 1, 0], 

733 [0, 1, 1, 1, 0], 

734 [0, 0, 0, 0, 0]]) 

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

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

737 array([[0, 1, 1, 1, 0], 

738 [1, 1, 1, 1, 1], 

739 [1, 1, 1, 1, 1], 

740 [1, 1, 1, 1, 1], 

741 [0, 1, 1, 1, 0]]) 

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

743 array([[0, 0, 0, 0, 0], 

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

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

746 [0, 1, 1, 1, 0], 

747 [0, 0, 0, 0, 0]]) 

748 

749 

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

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

752 >>> a 

753 array([[0, 0, 0, 0, 0, 0, 0], 

754 [0, 0, 1, 0, 1, 0, 0], 

755 [0, 0, 1, 0, 1, 0, 0], 

756 [0, 0, 1, 1, 1, 0, 0], 

757 [0, 0, 1, 1, 1, 0, 0], 

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

759 [0, 0, 0, 0, 0, 0, 0]]) 

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

761 >>> # coarsen boundaries with fine hollows. 

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

763 array([[0, 0, 0, 0, 0, 0, 0], 

764 [0, 0, 1, 0, 1, 0, 0], 

765 [0, 0, 1, 1, 1, 0, 0], 

766 [0, 0, 1, 1, 1, 0, 0], 

767 [0, 0, 1, 1, 1, 0, 0], 

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

769 [0, 0, 0, 0, 0, 0, 0]]) 

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

771 array([[0, 0, 0, 0, 0, 0, 0], 

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

773 [0, 0, 1, 1, 1, 0, 0], 

774 [0, 0, 1, 1, 1, 0, 0], 

775 [0, 0, 1, 1, 1, 0, 0], 

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

777 [0, 0, 0, 0, 0, 0, 0]]) 

778 

779 """ 

780 input = numpy.asarray(input) 

781 if structure is None: 

782 rank = input.ndim 

783 structure = generate_binary_structure(rank, 1) 

784 

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

786 border_value, origin, brute_force) 

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

788 border_value, origin, brute_force) 

789 

790 

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

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

793 """ 

794 Multidimensional binary hit-or-miss transform. 

795 

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

797 inside the input image. 

798 

799 Parameters 

800 ---------- 

801 input : array_like (cast to booleans) 

802 Binary image where a pattern is to be detected. 

803 structure1 : array_like (cast to booleans), optional 

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

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

806 structure of square connectivity 1 is chosen. 

807 structure2 : array_like (cast to booleans), optional 

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

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

810 `structure1` is taken. 

811 output : ndarray, optional 

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

813 By default, a new array is created. 

814 origin1 : int or tuple of ints, optional 

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

816 by default 0 for a centered structure. 

817 origin2 : int or tuple of ints, optional 

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

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

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

821 

822 Returns 

823 ------- 

824 binary_hit_or_miss : ndarray 

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

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

827 

828 See also 

829 -------- 

830 binary_erosion 

831 

832 References 

833 ---------- 

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

835 

836 Examples 

837 -------- 

838 >>> from scipy import ndimage 

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

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

841 >>> a 

842 array([[0, 0, 0, 0, 0, 0, 0], 

843 [0, 1, 0, 0, 0, 0, 0], 

844 [0, 0, 1, 1, 0, 0, 0], 

845 [0, 0, 1, 1, 0, 0, 0], 

846 [0, 0, 0, 0, 1, 1, 0], 

847 [0, 0, 0, 0, 1, 1, 0], 

848 [0, 0, 0, 0, 0, 0, 0]]) 

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

850 >>> structure1 

851 array([[1, 0, 0], 

852 [0, 1, 1], 

853 [0, 1, 1]]) 

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

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

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

857 [0, 0, 0, 0, 0, 0, 0], 

858 [0, 0, 1, 0, 0, 0, 0], 

859 [0, 0, 0, 0, 0, 0, 0], 

860 [0, 0, 0, 0, 1, 0, 0], 

861 [0, 0, 0, 0, 0, 0, 0], 

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

863 >>> # Change the origin of the filter 

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

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

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

867 array([[0, 0, 0, 0, 0, 0, 0], 

868 [0, 0, 0, 0, 0, 0, 0], 

869 [0, 0, 0, 0, 0, 0, 0], 

870 [0, 0, 0, 1, 0, 0, 0], 

871 [0, 0, 0, 0, 0, 0, 0], 

872 [0, 0, 0, 0, 0, 1, 0], 

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

874 

875 """ 

876 input = numpy.asarray(input) 

877 if structure1 is None: 

878 structure1 = generate_binary_structure(input.ndim, 1) 

879 if structure2 is None: 

880 structure2 = numpy.logical_not(structure1) 

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

882 if origin2 is None: 

883 origin2 = origin1 

884 else: 

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

886 

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

888 0, False) 

889 inplace = isinstance(output, numpy.ndarray) 

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

891 origin2, 1, False) 

892 if inplace: 

893 numpy.logical_not(output, output) 

894 numpy.logical_and(tmp1, output, output) 

895 else: 

896 numpy.logical_not(result, result) 

897 return numpy.logical_and(tmp1, result) 

898 

899 

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

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

902 """ 

903 Multidimensional binary propagation with the given structuring element. 

904 

905 Parameters 

906 ---------- 

907 input : array_like 

908 Binary image to be propagated inside `mask`. 

909 structure : array_like, optional 

910 Structuring element used in the successive dilations. The output 

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

912 several connex components. If no structuring element is 

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

914 to one. 

915 mask : array_like, optional 

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

917 propagate. 

918 output : ndarray, optional 

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

920 By default, a new array is created. 

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

922 Value at the border in the output array. 

923 origin : int or tuple of ints, optional 

924 Placement of the filter, by default 0. 

925 

926 Returns 

927 ------- 

928 binary_propagation : ndarray 

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

930 

931 Notes 

932 ----- 

933 This function is functionally equivalent to calling binary_dilation 

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

935 the result does not change anymore. 

936 

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

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

939 keeping the contours of larger objects untouched. 

940 

941 References 

942 ---------- 

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

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

945 image processing", 1998 

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

947 

948 Examples 

949 -------- 

950 >>> from scipy import ndimage 

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

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

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

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

955 >>> input 

956 array([[0, 0, 0, 0, 0, 0, 0, 0], 

957 [0, 0, 0, 0, 0, 0, 0, 0], 

958 [0, 0, 1, 0, 0, 0, 0, 0], 

959 [0, 0, 0, 0, 0, 0, 0, 0], 

960 [0, 0, 0, 0, 0, 0, 0, 0], 

961 [0, 0, 0, 0, 0, 0, 0, 0], 

962 [0, 0, 0, 0, 0, 0, 0, 0], 

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

964 >>> mask 

965 array([[0, 0, 0, 0, 0, 0, 0, 0], 

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

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

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

969 [0, 0, 0, 0, 1, 0, 0, 0], 

970 [0, 0, 0, 0, 0, 0, 0, 0], 

971 [0, 0, 0, 0, 0, 0, 1, 1], 

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

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

974 array([[0, 0, 0, 0, 0, 0, 0, 0], 

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

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

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

978 [0, 0, 0, 0, 0, 0, 0, 0], 

979 [0, 0, 0, 0, 0, 0, 0, 0], 

980 [0, 0, 0, 0, 0, 0, 0, 0], 

981 [0, 0, 0, 0, 0, 0, 0, 0]]) 

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

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

984 array([[0, 0, 0, 0, 0, 0, 0, 0], 

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

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

987 [0, 1, 1, 1, 0, 0, 0, 0], 

988 [0, 0, 0, 0, 1, 0, 0, 0], 

989 [0, 0, 0, 0, 0, 0, 0, 0], 

990 [0, 0, 0, 0, 0, 0, 0, 0], 

991 [0, 0, 0, 0, 0, 0, 0, 0]]) 

992 

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

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

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

996 >>> a 

997 array([[1, 0, 0, 0, 0, 0], 

998 [0, 0, 0, 0, 0, 0], 

999 [0, 0, 1, 1, 1, 0], 

1000 [0, 0, 1, 1, 1, 0], 

1001 [0, 0, 1, 1, 1, 0], 

1002 [0, 0, 0, 0, 0, 1]]) 

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

1004 array([[0, 0, 0, 0, 0, 0], 

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

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

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

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

1009 [0, 0, 0, 0, 0, 0]]) 

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

1011 >>> b.astype(int) 

1012 array([[0, 0, 0, 0, 0, 0], 

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

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

1015 [0, 0, 0, 1, 0, 0], 

1016 [0, 0, 0, 0, 0, 0], 

1017 [0, 0, 0, 0, 0, 0]]) 

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

1019 array([[0, 0, 0, 0, 0, 0], 

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

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

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

1023 [0, 0, 1, 1, 1, 0], 

1024 [0, 0, 0, 0, 0, 0]]) 

1025 

1026 """ 

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

1028 border_value, origin) 

1029 

1030 

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

1032 """ 

1033 Fill the holes in binary objects. 

1034 

1035 

1036 Parameters 

1037 ---------- 

1038 input : array_like 

1039 N-D binary array with holes to be filled 

1040 structure : array_like, optional 

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

1042 make computations faster but may miss holes separated from the 

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

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

1045 holes in the input have been filled. 

1046 output : ndarray, optional 

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

1048 By default, a new array is created. 

1049 origin : int, tuple of ints, optional 

1050 Position of the structuring element. 

1051 

1052 Returns 

1053 ------- 

1054 out : ndarray 

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

1056 filled. 

1057 

1058 See also 

1059 -------- 

1060 binary_dilation, binary_propagation, label 

1061 

1062 Notes 

1063 ----- 

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

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

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

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

1068 invaded region. 

1069 

1070 References 

1071 ---------- 

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

1073 

1074 

1075 Examples 

1076 -------- 

1077 >>> from scipy import ndimage 

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

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

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

1081 >>> a 

1082 array([[0, 0, 0, 0, 0], 

1083 [0, 1, 1, 1, 0], 

1084 [0, 1, 0, 1, 0], 

1085 [0, 1, 1, 1, 0], 

1086 [0, 0, 0, 0, 0]]) 

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

1088 array([[0, 0, 0, 0, 0], 

1089 [0, 1, 1, 1, 0], 

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

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

1092 [0, 0, 0, 0, 0]]) 

1093 >>> # Too big structuring element 

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

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

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

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

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

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

1100 

1101 """ 

1102 mask = numpy.logical_not(input) 

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

1104 inplace = isinstance(output, numpy.ndarray) 

1105 if inplace: 

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

1107 numpy.logical_not(output, output) 

1108 else: 

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

1110 origin) 

1111 numpy.logical_not(output, output) 

1112 return output 

1113 

1114 

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

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

1117 """ 

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

1119 or a footprint corresponding to a flat structuring element. 

1120 

1121 Grayscale erosion is a mathematical morphology operation. For the 

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

1123 as a minimum filter over a sliding window. 

1124 

1125 Parameters 

1126 ---------- 

1127 input : array_like 

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

1129 size : tuple of ints 

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

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

1132 footprint : array of ints, optional 

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

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

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

1136 structure : array of ints, optional 

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

1138 may be a non-flat structuring element. 

1139 output : array, optional 

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

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

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

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

1144 'constant'. Default is 'reflect' 

1145 cval : scalar, optional 

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

1147 is 0.0. 

1148 origin : scalar, optional 

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

1150 Default 0 

1151 

1152 Returns 

1153 ------- 

1154 output : ndarray 

1155 Grayscale erosion of `input`. 

1156 

1157 See also 

1158 -------- 

1159 binary_erosion, grey_dilation, grey_opening, grey_closing 

1160 generate_binary_structure, minimum_filter 

1161 

1162 Notes 

1163 ----- 

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

1165 over a domain E is given by: 

1166 

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

1168 

1169 In particular, for structuring elements defined as 

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

1171 input image inside a sliding window defined by E. 

1172 

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

1174 

1175 References 

1176 ---------- 

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

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

1179 

1180 Examples 

1181 -------- 

1182 >>> from scipy import ndimage 

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

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

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

1186 >>> a 

1187 array([[0, 0, 0, 0, 0, 0, 0], 

1188 [0, 3, 3, 3, 3, 3, 0], 

1189 [0, 3, 3, 1, 3, 3, 0], 

1190 [0, 3, 3, 3, 3, 3, 0], 

1191 [0, 3, 3, 3, 2, 3, 0], 

1192 [0, 3, 3, 3, 3, 3, 0], 

1193 [0, 0, 0, 0, 0, 0, 0]]) 

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

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

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

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

1198 [0, 0, 1, 1, 1, 0, 0], 

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

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

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

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

1203 >>> footprint 

1204 array([[False, True, False], 

1205 [ True, True, True], 

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

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

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

1209 array([[0, 0, 0, 0, 0, 0, 0], 

1210 [0, 0, 0, 0, 0, 0, 0], 

1211 [0, 0, 1, 1, 1, 0, 0], 

1212 [0, 0, 3, 1, 2, 0, 0], 

1213 [0, 0, 3, 2, 2, 0, 0], 

1214 [0, 0, 0, 0, 0, 0, 0], 

1215 [0, 0, 0, 0, 0, 0, 0]]) 

1216 

1217 """ 

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

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

1220 

1221 return filters._min_or_max_filter(input, size, footprint, structure, 

1222 output, mode, cval, origin, 1) 

1223 

1224 

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

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

1227 """ 

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

1229 or a footprint corresponding to a flat structuring element. 

1230 

1231 Grayscale dilation is a mathematical morphology operation. For the 

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

1233 as a maximum filter over a sliding window. 

1234 

1235 Parameters 

1236 ---------- 

1237 input : array_like 

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

1239 size : tuple of ints 

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

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

1242 footprint : array of ints, optional 

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

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

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

1246 structure : array of ints, optional 

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

1248 may be a non-flat structuring element. 

1249 output : array, optional 

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

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

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

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

1254 'constant'. Default is 'reflect' 

1255 cval : scalar, optional 

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

1257 is 0.0. 

1258 origin : scalar, optional 

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

1260 Default 0 

1261 

1262 Returns 

1263 ------- 

1264 grey_dilation : ndarray 

1265 Grayscale dilation of `input`. 

1266 

1267 See also 

1268 -------- 

1269 binary_dilation, grey_erosion, grey_closing, grey_opening 

1270 generate_binary_structure, maximum_filter 

1271 

1272 Notes 

1273 ----- 

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

1275 over a domain E is given by: 

1276 

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

1278 

1279 In particular, for structuring elements defined as 

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

1281 input image inside a sliding window defined by E. 

1282 

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

1284 

1285 References 

1286 ---------- 

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

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

1289 

1290 Examples 

1291 -------- 

1292 >>> from scipy import ndimage 

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

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

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

1296 >>> a 

1297 array([[0, 0, 0, 0, 0, 0, 0], 

1298 [0, 0, 0, 0, 0, 0, 0], 

1299 [0, 0, 1, 3, 1, 0, 0], 

1300 [0, 0, 1, 1, 1, 0, 0], 

1301 [0, 0, 1, 1, 2, 0, 0], 

1302 [0, 0, 0, 0, 0, 0, 0], 

1303 [0, 0, 0, 0, 0, 0, 0]]) 

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

1305 array([[0, 0, 0, 0, 0, 0, 0], 

1306 [0, 1, 3, 3, 3, 1, 0], 

1307 [0, 1, 3, 3, 3, 1, 0], 

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

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

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

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

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

1313 array([[0, 0, 0, 0, 0, 0, 0], 

1314 [0, 1, 3, 3, 3, 1, 0], 

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

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

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

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

1319 [0, 0, 0, 0, 0, 0, 0]]) 

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

1321 >>> s 

1322 array([[False, True, False], 

1323 [ True, True, True], 

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

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

1326 array([[0, 0, 0, 0, 0, 0, 0], 

1327 [0, 0, 1, 3, 1, 0, 0], 

1328 [0, 1, 3, 3, 3, 1, 0], 

1329 [0, 1, 1, 3, 2, 1, 0], 

1330 [0, 1, 1, 2, 2, 2, 0], 

1331 [0, 0, 1, 1, 2, 0, 0], 

1332 [0, 0, 0, 0, 0, 0, 0]]) 

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

1334 array([[1, 1, 1, 1, 1, 1, 1], 

1335 [1, 2, 4, 4, 4, 2, 1], 

1336 [1, 2, 4, 4, 4, 2, 1], 

1337 [1, 2, 4, 4, 4, 3, 1], 

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

1339 [1, 2, 2, 3, 3, 3, 1], 

1340 [1, 1, 1, 1, 1, 1, 1]]) 

1341 

1342 """ 

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

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

1345 if structure is not None: 

1346 structure = numpy.asarray(structure) 

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

1348 structure.ndim)] 

1349 if footprint is not None: 

1350 footprint = numpy.asarray(footprint) 

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

1352 footprint.ndim)] 

1353 

1354 input = numpy.asarray(input) 

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

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

1357 origin[ii] = -origin[ii] 

1358 if footprint is not None: 

1359 sz = footprint.shape[ii] 

1360 elif structure is not None: 

1361 sz = structure.shape[ii] 

1362 elif numpy.isscalar(size): 

1363 sz = size 

1364 else: 

1365 sz = size[ii] 

1366 if not sz & 1: 

1367 origin[ii] -= 1 

1368 

1369 return filters._min_or_max_filter(input, size, footprint, structure, 

1370 output, mode, cval, origin, 0) 

1371 

1372 

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

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

1375 """ 

1376 Multidimensional grayscale opening. 

1377 

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

1379 and a grayscale dilation. 

1380 

1381 Parameters 

1382 ---------- 

1383 input : array_like 

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

1385 size : tuple of ints 

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

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

1388 footprint : array of ints, optional 

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

1390 used for the grayscale opening. 

1391 structure : array of ints, optional 

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

1393 may be a non-flat structuring element. 

1394 output : array, optional 

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

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

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

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

1399 'constant'. Default is 'reflect' 

1400 cval : scalar, optional 

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

1402 is 0.0. 

1403 origin : scalar, optional 

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

1405 Default 0 

1406 

1407 Returns 

1408 ------- 

1409 grey_opening : ndarray 

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

1411 

1412 See also 

1413 -------- 

1414 binary_opening, grey_dilation, grey_erosion, grey_closing 

1415 generate_binary_structure 

1416 

1417 Notes 

1418 ----- 

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

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

1421 

1422 References 

1423 ---------- 

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

1425 

1426 Examples 

1427 -------- 

1428 >>> from scipy import ndimage 

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

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

1431 >>> a 

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

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

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

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

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

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

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

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

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

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

1442 [18, 19, 20, 22, 22, 22], 

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

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

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

1446 

1447 """ 

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

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

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

1451 cval, origin) 

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

1453 cval, origin) 

1454 

1455 

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

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

1458 """ 

1459 Multidimensional grayscale closing. 

1460 

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

1462 and a grayscale erosion. 

1463 

1464 Parameters 

1465 ---------- 

1466 input : array_like 

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

1468 size : tuple of ints 

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

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

1471 footprint : array of ints, optional 

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

1473 used for the grayscale closing. 

1474 structure : array of ints, optional 

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

1476 may be a non-flat structuring element. 

1477 output : array, optional 

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

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

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

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

1482 'constant'. Default is 'reflect' 

1483 cval : scalar, optional 

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

1485 is 0.0. 

1486 origin : scalar, optional 

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

1488 Default 0 

1489 

1490 Returns 

1491 ------- 

1492 grey_closing : ndarray 

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

1494 

1495 See also 

1496 -------- 

1497 binary_closing, grey_dilation, grey_erosion, grey_opening, 

1498 generate_binary_structure 

1499 

1500 Notes 

1501 ----- 

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

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

1504 

1505 References 

1506 ---------- 

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

1508 

1509 Examples 

1510 -------- 

1511 >>> from scipy import ndimage 

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

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

1514 >>> a 

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

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

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

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

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

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

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

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

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

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

1525 [19, 19, 20, 20, 22, 23], 

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

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

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

1529 

1530 """ 

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

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

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

1534 cval, origin) 

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

1536 cval, origin) 

1537 

1538 

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

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

1541 """ 

1542 Multidimensional morphological gradient. 

1543 

1544 The morphological gradient is calculated as the difference between a 

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

1546 

1547 Parameters 

1548 ---------- 

1549 input : array_like 

1550 Array over which to compute the morphlogical gradient. 

1551 size : tuple of ints 

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

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

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

1555 footprint : array of ints, optional 

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

1557 used for the morphology operations. Larger footprints 

1558 give a more blurred morphological gradient. 

1559 structure : array of ints, optional 

1560 Structuring element used for the morphology operations. 

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

1562 output : array, optional 

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

1564 may be provided. 

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

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

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

1568 'constant'. Default is 'reflect' 

1569 cval : scalar, optional 

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

1571 is 0.0. 

1572 origin : scalar, optional 

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

1574 Default 0 

1575 

1576 Returns 

1577 ------- 

1578 morphological_gradient : ndarray 

1579 Morphological gradient of `input`. 

1580 

1581 See also 

1582 -------- 

1583 grey_dilation, grey_erosion, gaussian_gradient_magnitude 

1584 

1585 Notes 

1586 ----- 

1587 For a flat structuring element, the morphological gradient 

1588 computed at a given point corresponds to the maximal difference 

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

1590 structuring element centered on the point. 

1591 

1592 References 

1593 ---------- 

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

1595 

1596 Examples 

1597 -------- 

1598 >>> from scipy import ndimage 

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

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

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

1602 array([[0, 0, 0, 0, 0, 0, 0], 

1603 [0, 1, 1, 1, 1, 1, 0], 

1604 [0, 1, 1, 1, 1, 1, 0], 

1605 [0, 1, 1, 0, 1, 1, 0], 

1606 [0, 1, 1, 1, 1, 1, 0], 

1607 [0, 1, 1, 1, 1, 1, 0], 

1608 [0, 0, 0, 0, 0, 0, 0]]) 

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

1610 >>> # between a dilation and an erosion 

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

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

1613 array([[0, 0, 0, 0, 0, 0, 0], 

1614 [0, 1, 1, 1, 1, 1, 0], 

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

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

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

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

1619 [0, 0, 0, 0, 0, 0, 0]]) 

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

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

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

1623 >>> a 

1624 array([[0, 0, 0, 0, 0, 0, 0], 

1625 [0, 0, 0, 0, 0, 0, 0], 

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

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

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

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

1630 [0, 0, 0, 0, 0, 0, 0]]) 

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

1632 array([[0, 0, 0, 0, 0, 0, 0], 

1633 [0, 1, 3, 3, 3, 1, 0], 

1634 [0, 1, 3, 3, 3, 1, 0], 

1635 [0, 1, 3, 2, 3, 2, 0], 

1636 [0, 1, 1, 2, 2, 2, 0], 

1637 [0, 1, 1, 2, 2, 2, 0], 

1638 [0, 0, 0, 0, 0, 0, 0]]) 

1639 

1640 """ 

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

1642 cval, origin) 

1643 if isinstance(output, numpy.ndarray): 

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

1645 cval, origin) 

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

1647 else: 

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

1649 None, mode, cval, origin)) 

1650 

1651 

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

1653 structure=None, output=None, 

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

1655 """ 

1656 Multidimensional morphological laplace. 

1657 

1658 Parameters 

1659 ---------- 

1660 input : array_like 

1661 Input. 

1662 size : int or sequence of ints, optional 

1663 See `structure`. 

1664 footprint : bool or ndarray, optional 

1665 See `structure`. 

1666 structure : structure, optional 

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

1668 output : ndarray, optional 

1669 An output array can optionally be provided. 

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

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

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

1673 Default is 'reflect'. 

1674 cval : scalar, optional 

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

1676 Default is 0.0 

1677 origin : origin, optional 

1678 The origin parameter controls the placement of the filter. 

1679 

1680 Returns 

1681 ------- 

1682 morphological_laplace : ndarray 

1683 Output 

1684 

1685 """ 

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

1687 cval, origin) 

1688 if isinstance(output, numpy.ndarray): 

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

1690 cval, origin) 

1691 numpy.add(tmp1, output, output) 

1692 numpy.subtract(output, input, output) 

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

1694 else: 

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

1696 cval, origin) 

1697 numpy.add(tmp1, tmp2, tmp2) 

1698 numpy.subtract(tmp2, input, tmp2) 

1699 numpy.subtract(tmp2, input, tmp2) 

1700 return tmp2 

1701 

1702 

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

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

1705 """ 

1706 Multidimensional white tophat filter. 

1707 

1708 Parameters 

1709 ---------- 

1710 input : array_like 

1711 Input. 

1712 size : tuple of ints 

1713 Shape of a flat and full structuring element used for the filter. 

1714 Optional if `footprint` or `structure` is provided. 

1715 footprint : array of ints, optional 

1716 Positions of elements of a flat structuring element 

1717 used for the white tophat filter. 

1718 structure : array of ints, optional 

1719 Structuring element used for the filter. `structure` 

1720 may be a non-flat structuring element. 

1721 output : array, optional 

1722 An array used for storing the output of the filter may be provided. 

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

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

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

1726 'constant'. Default is 'reflect' 

1727 cval : scalar, optional 

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

1729 Default is 0.0. 

1730 origin : scalar, optional 

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

1732 Default is 0. 

1733 

1734 Returns 

1735 ------- 

1736 output : ndarray 

1737 Result of the filter of `input` with `structure`. 

1738 

1739 See also 

1740 -------- 

1741 black_tophat 

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 tmp = grey_erosion(input, size, footprint, structure, None, mode, 

1747 cval, origin) 

1748 tmp = grey_dilation(tmp, size, footprint, structure, output, mode, 

1749 cval, origin) 

1750 if tmp is None: 

1751 tmp = output 

1752 

1753 if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_: 

1754 numpy.bitwise_xor(input, tmp, out=tmp) 

1755 else: 

1756 numpy.subtract(input, tmp, out=tmp) 

1757 return tmp 

1758 

1759 

1760def black_tophat(input, size=None, footprint=None, 

1761 structure=None, output=None, mode="reflect", 

1762 cval=0.0, origin=0): 

1763 """ 

1764 Multidimensional black tophat filter. 

1765 

1766 Parameters 

1767 ---------- 

1768 input : array_like 

1769 Input. 

1770 size : tuple of ints, optional 

1771 Shape of a flat and full structuring element used for the filter. 

1772 Optional if `footprint` or `structure` is provided. 

1773 footprint : array of ints, optional 

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

1775 used for the black tophat filter. 

1776 structure : array of ints, optional 

1777 Structuring element used for the filter. `structure` 

1778 may be a non-flat structuring element. 

1779 output : array, optional 

1780 An array used for storing the output of the filter may be provided. 

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

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

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

1784 'constant'. Default is 'reflect' 

1785 cval : scalar, optional 

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

1787 is 0.0. 

1788 origin : scalar, optional 

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

1790 Default 0 

1791 

1792 Returns 

1793 ------- 

1794 black_tophat : ndarray 

1795 Result of the filter of `input` with `structure`. 

1796 

1797 See also 

1798 -------- 

1799 white_tophat, grey_opening, grey_closing 

1800 

1801 """ 

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

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

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

1805 cval, origin) 

1806 tmp = grey_erosion(tmp, size, footprint, structure, output, mode, 

1807 cval, origin) 

1808 if tmp is None: 

1809 tmp = output 

1810 

1811 if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_: 

1812 numpy.bitwise_xor(tmp, input, out=tmp) 

1813 else: 

1814 numpy.subtract(tmp, input, out=tmp) 

1815 return tmp 

1816 

1817 

1818def distance_transform_bf(input, metric="euclidean", sampling=None, 

1819 return_distances=True, return_indices=False, 

1820 distances=None, indices=None): 

1821 """ 

1822 Distance transform function by a brute force algorithm. 

1823 

1824 This function calculates the distance transform of the `input`, by 

1825 replacing each foreground (non-zero) element, with its 

1826 shortest distance to the background (any zero-valued element). 

1827 

1828 In addition to the distance transform, the feature transform can 

1829 be calculated. In this case the index of the closest background 

1830 element is returned along the first axis of the result. 

1831 

1832 Parameters 

1833 ---------- 

1834 input : array_like 

1835 Input 

1836 metric : str, optional 

1837 Three types of distance metric are supported: 'euclidean', 'taxicab', 

1838 and 'chessboard'. 

1839 sampling : {int, sequence of ints}, optional 

1840 This parameter is only used in the case of the euclidean `metric` 

1841 distance transform. 

1842 

1843 The sampling along each axis can be given by the `sampling` parameter 

1844 which should be a sequence of length equal to the input rank, or a 

1845 single number in which the `sampling` is assumed to be equal along all 

1846 axes. 

1847 return_distances : bool, optional 

1848 The `return_distances` flag can be used to indicate if the distance 

1849 transform is returned. 

1850 

1851 The default is True. 

1852 return_indices : bool, optional 

1853 The `return_indices` flags can be used to indicate if the feature 

1854 transform is returned. 

1855 

1856 The default is False. 

1857 distances : float64 ndarray, optional 

1858 Optional output array to hold distances (if `return_distances` is 

1859 True). 

1860 indices : int64 ndarray, optional 

1861 Optional output array to hold indices (if `return_indices` is True). 

1862 

1863 Returns 

1864 ------- 

1865 distances : ndarray 

1866 Distance array if `return_distances` is True. 

1867 indices : ndarray 

1868 Indices array if `return_indices` is True. 

1869 

1870 Notes 

1871 ----- 

1872 This function employs a slow brute force algorithm, see also the 

1873 function distance_transform_cdt for more efficient taxicab and 

1874 chessboard algorithms. 

1875 

1876 """ 

1877 if (not return_distances) and (not return_indices): 

1878 msg = 'at least one of distances/indices must be specified' 

1879 raise RuntimeError(msg) 

1880 

1881 tmp1 = numpy.asarray(input) != 0 

1882 struct = generate_binary_structure(tmp1.ndim, tmp1.ndim) 

1883 tmp2 = binary_dilation(tmp1, struct) 

1884 tmp2 = numpy.logical_xor(tmp1, tmp2) 

1885 tmp1 = tmp1.astype(numpy.int8) - tmp2.astype(numpy.int8) 

1886 metric = metric.lower() 

1887 if metric == 'euclidean': 

1888 metric = 1 

1889 elif metric in ['taxicab', 'cityblock', 'manhattan']: 

1890 metric = 2 

1891 elif metric == 'chessboard': 

1892 metric = 3 

1893 else: 

1894 raise RuntimeError('distance metric not supported') 

1895 if sampling is not None: 

1896 sampling = _ni_support._normalize_sequence(sampling, tmp1.ndim) 

1897 sampling = numpy.asarray(sampling, dtype=numpy.float64) 

1898 if not sampling.flags.contiguous: 

1899 sampling = sampling.copy() 

1900 if return_indices: 

1901 ft = numpy.zeros(tmp1.shape, dtype=numpy.int32) 

1902 else: 

1903 ft = None 

1904 if return_distances: 

1905 if distances is None: 

1906 if metric == 1: 

1907 dt = numpy.zeros(tmp1.shape, dtype=numpy.float64) 

1908 else: 

1909 dt = numpy.zeros(tmp1.shape, dtype=numpy.uint32) 

1910 else: 

1911 if distances.shape != tmp1.shape: 

1912 raise RuntimeError('distances array has wrong shape') 

1913 if metric == 1: 

1914 if distances.dtype.type != numpy.float64: 

1915 raise RuntimeError('distances array must be float64') 

1916 else: 

1917 if distances.dtype.type != numpy.uint32: 

1918 raise RuntimeError('distances array must be uint32') 

1919 dt = distances 

1920 else: 

1921 dt = None 

1922 

1923 _nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft) 

1924 if return_indices: 

1925 if isinstance(indices, numpy.ndarray): 

1926 if indices.dtype.type != numpy.int32: 

1927 raise RuntimeError('indices must of int32 type') 

1928 if indices.shape != (tmp1.ndim,) + tmp1.shape: 

1929 raise RuntimeError('indices has wrong shape') 

1930 tmp2 = indices 

1931 else: 

1932 tmp2 = numpy.indices(tmp1.shape, dtype=numpy.int32) 

1933 ft = numpy.ravel(ft) 

1934 for ii in range(tmp2.shape[0]): 

1935 rtmp = numpy.ravel(tmp2[ii, ...])[ft] 

1936 rtmp.shape = tmp1.shape 

1937 tmp2[ii, ...] = rtmp 

1938 ft = tmp2 

1939 

1940 # construct and return the result 

1941 result = [] 

1942 if return_distances and not isinstance(distances, numpy.ndarray): 

1943 result.append(dt) 

1944 if return_indices and not isinstance(indices, numpy.ndarray): 

1945 result.append(ft) 

1946 

1947 if len(result) == 2: 

1948 return tuple(result) 

1949 elif len(result) == 1: 

1950 return result[0] 

1951 else: 

1952 return None 

1953 

1954 

1955def distance_transform_cdt(input, metric='chessboard', return_distances=True, 

1956 return_indices=False, distances=None, indices=None): 

1957 """ 

1958 Distance transform for chamfer type of transforms. 

1959 

1960 Parameters 

1961 ---------- 

1962 input : array_like 

1963 Input 

1964 metric : {'chessboard', 'taxicab'}, optional 

1965 The `metric` determines the type of chamfering that is done. If the 

1966 `metric` is equal to 'taxicab' a structure is generated using 

1967 generate_binary_structure with a squared distance equal to 1. If 

1968 the `metric` is equal to 'chessboard', a `metric` is generated 

1969 using generate_binary_structure with a squared distance equal to 

1970 the dimensionality of the array. These choices correspond to the 

1971 common interpretations of the 'taxicab' and the 'chessboard' 

1972 distance metrics in two dimensions. 

1973 

1974 The default for `metric` is 'chessboard'. 

1975 return_distances, return_indices : bool, optional 

1976 The `return_distances`, and `return_indices` flags can be used to 

1977 indicate if the distance transform, the feature transform, or both 

1978 must be returned. 

1979 

1980 If the feature transform is returned (``return_indices=True``), 

1981 the index of the closest background element is returned along 

1982 the first axis of the result. 

1983 

1984 The `return_distances` default is True, and the 

1985 `return_indices` default is False. 

1986 distances, indices : ndarrays of int32, optional 

1987 The `distances` and `indices` arguments can be used to give optional 

1988 output arrays that must be the same shape as `input`. 

1989 

1990 """ 

1991 if (not return_distances) and (not return_indices): 

1992 msg = 'at least one of distances/indices must be specified' 

1993 raise RuntimeError(msg) 

1994 

1995 ft_inplace = isinstance(indices, numpy.ndarray) 

1996 dt_inplace = isinstance(distances, numpy.ndarray) 

1997 input = numpy.asarray(input) 

1998 if metric in ['taxicab', 'cityblock', 'manhattan']: 

1999 rank = input.ndim 

2000 metric = generate_binary_structure(rank, 1) 

2001 elif metric == 'chessboard': 

2002 rank = input.ndim 

2003 metric = generate_binary_structure(rank, rank) 

2004 else: 

2005 try: 

2006 metric = numpy.asarray(metric) 

2007 except Exception: 

2008 raise RuntimeError('invalid metric provided') 

2009 for s in metric.shape: 

2010 if s != 3: 

2011 raise RuntimeError('metric sizes must be equal to 3') 

2012 

2013 if not metric.flags.contiguous: 

2014 metric = metric.copy() 

2015 if dt_inplace: 

2016 if distances.dtype.type != numpy.int32: 

2017 raise RuntimeError('distances must be of int32 type') 

2018 if distances.shape != input.shape: 

2019 raise RuntimeError('distances has wrong shape') 

2020 dt = distances 

2021 dt[...] = numpy.where(input, -1, 0).astype(numpy.int32) 

2022 else: 

2023 dt = numpy.where(input, -1, 0).astype(numpy.int32) 

2024 

2025 rank = dt.ndim 

2026 if return_indices: 

2027 sz = numpy.prod(dt.shape, axis=0) 

2028 ft = numpy.arange(sz, dtype=numpy.int32) 

2029 ft.shape = dt.shape 

2030 else: 

2031 ft = None 

2032 

2033 _nd_image.distance_transform_op(metric, dt, ft) 

2034 dt = dt[tuple([slice(None, None, -1)] * rank)] 

2035 if return_indices: 

2036 ft = ft[tuple([slice(None, None, -1)] * rank)] 

2037 _nd_image.distance_transform_op(metric, dt, ft) 

2038 dt = dt[tuple([slice(None, None, -1)] * rank)] 

2039 if return_indices: 

2040 ft = ft[tuple([slice(None, None, -1)] * rank)] 

2041 ft = numpy.ravel(ft) 

2042 if ft_inplace: 

2043 if indices.dtype.type != numpy.int32: 

2044 raise RuntimeError('indices must of int32 type') 

2045 if indices.shape != (dt.ndim,) + dt.shape: 

2046 raise RuntimeError('indices has wrong shape') 

2047 tmp = indices 

2048 else: 

2049 tmp = numpy.indices(dt.shape, dtype=numpy.int32) 

2050 for ii in range(tmp.shape[0]): 

2051 rtmp = numpy.ravel(tmp[ii, ...])[ft] 

2052 rtmp.shape = dt.shape 

2053 tmp[ii, ...] = rtmp 

2054 ft = tmp 

2055 

2056 # construct and return the result 

2057 result = [] 

2058 if return_distances and not dt_inplace: 

2059 result.append(dt) 

2060 if return_indices and not ft_inplace: 

2061 result.append(ft) 

2062 

2063 if len(result) == 2: 

2064 return tuple(result) 

2065 elif len(result) == 1: 

2066 return result[0] 

2067 else: 

2068 return None 

2069 

2070 

2071def distance_transform_edt(input, sampling=None, return_distances=True, 

2072 return_indices=False, distances=None, indices=None): 

2073 """ 

2074 Exact Euclidean distance transform. 

2075 

2076 In addition to the distance transform, the feature transform can 

2077 be calculated. In this case the index of the closest background 

2078 element is returned along the first axis of the result. 

2079 

2080 Parameters 

2081 ---------- 

2082 input : array_like 

2083 Input data to transform. Can be any type but will be converted 

2084 into binary: 1 wherever input equates to True, 0 elsewhere. 

2085 sampling : float or int, or sequence of same, optional 

2086 Spacing of elements along each dimension. If a sequence, must be of 

2087 length equal to the input rank; if a single number, this is used for 

2088 all axes. If not specified, a grid spacing of unity is implied. 

2089 return_distances : bool, optional 

2090 Whether to return distance matrix. At least one of 

2091 return_distances/return_indices must be True. Default is True. 

2092 return_indices : bool, optional 

2093 Whether to return indices matrix. Default is False. 

2094 distances : ndarray, optional 

2095 Used for output of distance array, must be of type float64. 

2096 indices : ndarray, optional 

2097 Used for output of indices, must be of type int32. 

2098 

2099 Returns 

2100 ------- 

2101 distance_transform_edt : ndarray or list of ndarrays 

2102 Either distance matrix, index matrix, or a list of the two, 

2103 depending on `return_x` flags and `distance` and `indices` 

2104 input parameters. 

2105 

2106 Notes 

2107 ----- 

2108 The Euclidean distance transform gives values of the Euclidean 

2109 distance:: 

2110 

2111 n 

2112 y_i = sqrt(sum (x[i]-b[i])**2) 

2113 i 

2114 

2115 where b[i] is the background point (value 0) with the smallest 

2116 Euclidean distance to input points x[i], and n is the 

2117 number of dimensions. 

2118 

2119 Examples 

2120 -------- 

2121 >>> from scipy import ndimage 

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

2123 ... [0,0,1,1,1], 

2124 ... [0,1,1,1,1], 

2125 ... [0,1,1,1,0], 

2126 ... [0,1,1,0,0])) 

2127 >>> ndimage.distance_transform_edt(a) 

2128 array([[ 0. , 1. , 1.4142, 2.2361, 3. ], 

2129 [ 0. , 0. , 1. , 2. , 2. ], 

2130 [ 0. , 1. , 1.4142, 1.4142, 1. ], 

2131 [ 0. , 1. , 1.4142, 1. , 0. ], 

2132 [ 0. , 1. , 1. , 0. , 0. ]]) 

2133 

2134 With a sampling of 2 units along x, 1 along y: 

2135 

2136 >>> ndimage.distance_transform_edt(a, sampling=[2,1]) 

2137 array([[ 0. , 1. , 2. , 2.8284, 3.6056], 

2138 [ 0. , 0. , 1. , 2. , 3. ], 

2139 [ 0. , 1. , 2. , 2.2361, 2. ], 

2140 [ 0. , 1. , 2. , 1. , 0. ], 

2141 [ 0. , 1. , 1. , 0. , 0. ]]) 

2142 

2143 Asking for indices as well: 

2144 

2145 >>> edt, inds = ndimage.distance_transform_edt(a, return_indices=True) 

2146 >>> inds 

2147 array([[[0, 0, 1, 1, 3], 

2148 [1, 1, 1, 1, 3], 

2149 [2, 2, 1, 3, 3], 

2150 [3, 3, 4, 4, 3], 

2151 [4, 4, 4, 4, 4]], 

2152 [[0, 0, 1, 1, 4], 

2153 [0, 1, 1, 1, 4], 

2154 [0, 0, 1, 4, 4], 

2155 [0, 0, 3, 3, 4], 

2156 [0, 0, 3, 3, 4]]]) 

2157 

2158 With arrays provided for inplace outputs: 

2159 

2160 >>> indices = np.zeros(((np.ndim(a),) + a.shape), dtype=np.int32) 

2161 >>> ndimage.distance_transform_edt(a, return_indices=True, indices=indices) 

2162 array([[ 0. , 1. , 1.4142, 2.2361, 3. ], 

2163 [ 0. , 0. , 1. , 2. , 2. ], 

2164 [ 0. , 1. , 1.4142, 1.4142, 1. ], 

2165 [ 0. , 1. , 1.4142, 1. , 0. ], 

2166 [ 0. , 1. , 1. , 0. , 0. ]]) 

2167 >>> indices 

2168 array([[[0, 0, 1, 1, 3], 

2169 [1, 1, 1, 1, 3], 

2170 [2, 2, 1, 3, 3], 

2171 [3, 3, 4, 4, 3], 

2172 [4, 4, 4, 4, 4]], 

2173 [[0, 0, 1, 1, 4], 

2174 [0, 1, 1, 1, 4], 

2175 [0, 0, 1, 4, 4], 

2176 [0, 0, 3, 3, 4], 

2177 [0, 0, 3, 3, 4]]]) 

2178 

2179 """ 

2180 if (not return_distances) and (not return_indices): 

2181 msg = 'at least one of distances/indices must be specified' 

2182 raise RuntimeError(msg) 

2183 

2184 ft_inplace = isinstance(indices, numpy.ndarray) 

2185 dt_inplace = isinstance(distances, numpy.ndarray) 

2186 # calculate the feature transform 

2187 input = numpy.atleast_1d(numpy.where(input, 1, 0).astype(numpy.int8)) 

2188 if sampling is not None: 

2189 sampling = _ni_support._normalize_sequence(sampling, input.ndim) 

2190 sampling = numpy.asarray(sampling, dtype=numpy.float64) 

2191 if not sampling.flags.contiguous: 

2192 sampling = sampling.copy() 

2193 

2194 if ft_inplace: 

2195 ft = indices 

2196 if ft.shape != (input.ndim,) + input.shape: 

2197 raise RuntimeError('indices has wrong shape') 

2198 if ft.dtype.type != numpy.int32: 

2199 raise RuntimeError('indices must be of int32 type') 

2200 else: 

2201 ft = numpy.zeros((input.ndim,) + input.shape, dtype=numpy.int32) 

2202 

2203 _nd_image.euclidean_feature_transform(input, sampling, ft) 

2204 # if requested, calculate the distance transform 

2205 if return_distances: 

2206 dt = ft - numpy.indices(input.shape, dtype=ft.dtype) 

2207 dt = dt.astype(numpy.float64) 

2208 if sampling is not None: 

2209 for ii in range(len(sampling)): 

2210 dt[ii, ...] *= sampling[ii] 

2211 numpy.multiply(dt, dt, dt) 

2212 if dt_inplace: 

2213 dt = numpy.add.reduce(dt, axis=0) 

2214 if distances.shape != dt.shape: 

2215 raise RuntimeError('indices has wrong shape') 

2216 if distances.dtype.type != numpy.float64: 

2217 raise RuntimeError('indices must be of float64 type') 

2218 numpy.sqrt(dt, distances) 

2219 else: 

2220 dt = numpy.add.reduce(dt, axis=0) 

2221 dt = numpy.sqrt(dt) 

2222 

2223 # construct and return the result 

2224 result = [] 

2225 if return_distances and not dt_inplace: 

2226 result.append(dt) 

2227 if return_indices and not ft_inplace: 

2228 result.append(ft) 

2229 

2230 if len(result) == 2: 

2231 return tuple(result) 

2232 elif len(result) == 1: 

2233 return result[0] 

2234 else: 

2235 return None