Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/scipy/ndimage/morphology.py : 7%

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.
31import warnings
32import operator
34import numpy
35from . import _ni_support
36from . import _nd_image
37from . import filters
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']
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])
55def iterate_structure(structure, iterations, origin=None):
56 """
57 Iterate a structure by dilating it with itself.
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.
71 Returns
72 -------
73 iterate_structure : ndarray of bools
74 A new structuring element obtained by dilating `structure`
75 (`iterations` - 1) times with itself.
77 See also
78 --------
79 generate_binary_structure
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]])
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
124def generate_binary_structure(rank, connectivity):
125 """
126 Generate a binary structure for binary morphological operations.
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).
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.
147 See also
148 --------
149 iterate_structure, binary_dilation, binary_erosion
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`.
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)
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
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')
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
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.
298 Binary erosion is a mathematical morphology operation used for image
299 processing.
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.
331 Returns
332 -------
333 binary_erosion : ndarray of bools
334 Erosion of the input by the structuring element.
336 See also
337 --------
338 grey_erosion, binary_dilation, binary_closing, binary_opening,
339 generate_binary_structure
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.
349 References
350 ----------
351 .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29
352 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
385 """
386 return _binary_erosion(input, structure, iterations, mask,
387 output, border_value, origin, 0, brute_force)
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.
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.
427 Returns
428 -------
429 binary_dilation : ndarray of bools
430 Dilation of the input by the structuring element.
432 See also
433 --------
434 grey_dilation, binary_erosion, binary_closing, binary_opening,
435 generate_binary_structure
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.
445 References
446 ----------
447 .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29
448 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
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.]])
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
518 return _binary_erosion(input, structure, iterations, mask,
519 output, border_value, origin, 1, brute_force)
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.
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.
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.
555 .. versionadded:: 1.1.0
556 border_value : int (cast to 0 or 1), optional
557 Value at the border in the output array.
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.
567 .. versionadded:: 1.1.0
569 Returns
570 -------
571 binary_opening : ndarray of bools
572 Opening of the input by the structuring element.
574 See also
575 --------
576 grey_opening, binary_closing, binary_erosion, binary_dilation,
577 generate_binary_structure
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.
586 Together with *closing* (`binary_closing`), opening can be used for
587 noise removal.
589 References
590 ----------
591 .. [1] https://en.wikipedia.org/wiki/Opening_%28morphology%29
592 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
633 """
634 input = numpy.asarray(input)
635 if structure is None:
636 rank = input.ndim
637 structure = generate_binary_structure(rank, 1)
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)
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.
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.
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.
678 .. versionadded:: 1.1.0
679 border_value : int (cast to 0 or 1), optional
680 Value at the border in the output array.
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.
690 .. versionadded:: 1.1.0
692 Returns
693 -------
694 binary_closing : ndarray of bools
695 Closing of the input by the structuring element.
697 See also
698 --------
699 grey_closing, binary_opening, binary_dilation, binary_erosion,
700 generate_binary_structure
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.
709 Together with *opening* (`binary_opening`), closing can be used for
710 noise removal.
712 References
713 ----------
714 .. [1] https://en.wikipedia.org/wiki/Closing_%28morphology%29
715 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
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]])
779 """
780 input = numpy.asarray(input)
781 if structure is None:
782 rank = input.ndim
783 structure = generate_binary_structure(rank, 1)
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)
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.
796 The hit-or-miss transform finds the locations of a given pattern
797 inside the input image.
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`.
822 Returns
823 -------
824 binary_hit_or_miss : ndarray
825 Hit-or-miss transform of `input` with the given structuring
826 element (`structure1`, `structure2`).
828 See also
829 --------
830 binary_erosion
832 References
833 ----------
834 .. [1] https://en.wikipedia.org/wiki/Hit-or-miss_transform
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]])
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)
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)
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.
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.
926 Returns
927 -------
928 binary_propagation : ndarray
929 Binary propagation of `input` inside `mask`.
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.
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.
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
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]])
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]])
1026 """
1027 return binary_dilation(input, structure, -1, mask, output,
1028 border_value, origin)
1031def binary_fill_holes(input, structure=None, output=None, origin=0):
1032 """
1033 Fill the holes in binary objects.
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.
1052 Returns
1053 -------
1054 out : ndarray
1055 Transformation of the initial image `input` where holes have been
1056 filled.
1058 See also
1059 --------
1060 binary_dilation, binary_propagation, label
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.
1070 References
1071 ----------
1072 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
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
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.
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.
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
1152 Returns
1153 -------
1154 output : ndarray
1155 Grayscale erosion of `input`.
1157 See also
1158 --------
1159 binary_erosion, grey_dilation, grey_opening, grey_closing
1160 generate_binary_structure, minimum_filter
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:
1167 (input+s)(x) = min {input(y) - s(x-y), for y in E}
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.
1173 Grayscale erosion [1]_ is a *mathematical morphology* operation [2]_.
1175 References
1176 ----------
1177 .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29
1178 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
1217 """
1218 if size is None and footprint is None and structure is None:
1219 raise ValueError("size, footprint, or structure must be specified")
1221 return filters._min_or_max_filter(input, size, footprint, structure,
1222 output, mode, cval, origin, 1)
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.
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.
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
1262 Returns
1263 -------
1264 grey_dilation : ndarray
1265 Grayscale dilation of `input`.
1267 See also
1268 --------
1269 binary_dilation, grey_erosion, grey_closing, grey_opening
1270 generate_binary_structure, maximum_filter
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:
1277 (input+s)(x) = max {input(y) + s(x-y), for y in E}
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.
1283 Grayscale dilation [1]_ is a *mathematical morphology* operation [2]_.
1285 References
1286 ----------
1287 .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29
1288 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
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)]
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
1369 return filters._min_or_max_filter(input, size, footprint, structure,
1370 output, mode, cval, origin, 0)
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.
1378 A grayscale opening consists in the succession of a grayscale erosion,
1379 and a grayscale dilation.
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
1407 Returns
1408 -------
1409 grey_opening : ndarray
1410 Result of the grayscale opening of `input` with `structure`.
1412 See also
1413 --------
1414 binary_opening, grey_dilation, grey_erosion, grey_closing
1415 generate_binary_structure
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.
1422 References
1423 ----------
1424 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
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
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)
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.
1461 A grayscale closing consists in the succession of a grayscale dilation,
1462 and a grayscale erosion.
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
1490 Returns
1491 -------
1492 grey_closing : ndarray
1493 Result of the grayscale closing of `input` with `structure`.
1495 See also
1496 --------
1497 binary_closing, grey_dilation, grey_erosion, grey_opening,
1498 generate_binary_structure
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.
1505 References
1506 ----------
1507 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
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
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)
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.
1544 The morphological gradient is calculated as the difference between a
1545 dilation and an erosion of the input with a given structuring element.
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
1576 Returns
1577 -------
1578 morphological_gradient : ndarray
1579 Morphological gradient of `input`.
1581 See also
1582 --------
1583 grey_dilation, grey_erosion, gaussian_gradient_magnitude
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.
1592 References
1593 ----------
1594 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
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]])
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))
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.
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.
1680 Returns
1681 -------
1682 morphological_laplace : ndarray
1683 Output
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
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.
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.
1734 Returns
1735 -------
1736 output : ndarray
1737 Result of the filter of `input` with `structure`.
1739 See also
1740 --------
1741 black_tophat
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
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
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.
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
1792 Returns
1793 -------
1794 black_tophat : ndarray
1795 Result of the filter of `input` with `structure`.
1797 See also
1798 --------
1799 white_tophat, grey_opening, grey_closing
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
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
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.
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).
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.
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.
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.
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.
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).
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.
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.
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)
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
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
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)
1947 if len(result) == 2:
1948 return tuple(result)
1949 elif len(result) == 1:
1950 return result[0]
1951 else:
1952 return None
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.
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.
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.
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.
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`.
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)
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')
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)
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
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
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)
2063 if len(result) == 2:
2064 return tuple(result)
2065 elif len(result) == 1:
2066 return result[0]
2067 else:
2068 return None
2071def distance_transform_edt(input, sampling=None, return_distances=True,
2072 return_indices=False, distances=None, indices=None):
2073 """
2074 Exact Euclidean distance transform.
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.
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.
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.
2106 Notes
2107 -----
2108 The Euclidean distance transform gives values of the Euclidean
2109 distance::
2111 n
2112 y_i = sqrt(sum (x[i]-b[i])**2)
2113 i
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.
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. ]])
2134 With a sampling of 2 units along x, 1 along y:
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. ]])
2143 Asking for indices as well:
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]]])
2158 With arrays provided for inplace outputs:
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]]])
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)
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()
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)
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)
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)
2230 if len(result) == 2:
2231 return tuple(result)
2232 elif len(result) == 1:
2233 return result[0]
2234 else:
2235 return None