Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/ndimage/_morphology.py: 7%
413 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
1# Copyright (C) 2003-2005 Peter J. Verveer
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6#
7# 1. Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#
10# 2. Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following
12# disclaimer in the documentation and/or other materials provided
13# with the distribution.
14#
15# 3. The name of the author may not be used to endorse or promote
16# products derived from this software without specific prior
17# written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 >>> import numpy as np
163 >>> struct = ndimage.generate_binary_structure(2, 1)
164 >>> struct
165 array([[False, True, False],
166 [ True, True, True],
167 [False, True, False]], dtype=bool)
168 >>> a = np.zeros((5,5))
169 >>> a[2, 2] = 1
170 >>> a
171 array([[ 0., 0., 0., 0., 0.],
172 [ 0., 0., 0., 0., 0.],
173 [ 0., 0., 1., 0., 0.],
174 [ 0., 0., 0., 0., 0.],
175 [ 0., 0., 0., 0., 0.]])
176 >>> b = ndimage.binary_dilation(a, structure=struct).astype(a.dtype)
177 >>> b
178 array([[ 0., 0., 0., 0., 0.],
179 [ 0., 0., 1., 0., 0.],
180 [ 0., 1., 1., 1., 0.],
181 [ 0., 0., 1., 0., 0.],
182 [ 0., 0., 0., 0., 0.]])
183 >>> ndimage.binary_dilation(b, structure=struct).astype(a.dtype)
184 array([[ 0., 0., 1., 0., 0.],
185 [ 0., 1., 1., 1., 0.],
186 [ 1., 1., 1., 1., 1.],
187 [ 0., 1., 1., 1., 0.],
188 [ 0., 0., 1., 0., 0.]])
189 >>> struct = ndimage.generate_binary_structure(2, 2)
190 >>> struct
191 array([[ True, True, True],
192 [ True, True, True],
193 [ True, True, True]], dtype=bool)
194 >>> struct = ndimage.generate_binary_structure(3, 1)
195 >>> struct # no diagonal elements
196 array([[[False, False, False],
197 [False, True, False],
198 [False, False, False]],
199 [[False, True, False],
200 [ True, True, True],
201 [False, True, False]],
202 [[False, False, False],
203 [False, True, False],
204 [False, False, False]]], dtype=bool)
206 """
207 if connectivity < 1:
208 connectivity = 1
209 if rank < 1:
210 return numpy.array(True, dtype=bool)
211 output = numpy.fabs(numpy.indices([3] * rank) - 1)
212 output = numpy.add.reduce(output, 0)
213 return output <= connectivity
216def _binary_erosion(input, structure, iterations, mask, output,
217 border_value, origin, invert, brute_force):
218 try:
219 iterations = operator.index(iterations)
220 except TypeError as e:
221 raise TypeError('iterations parameter should be an integer') from e
223 input = numpy.asarray(input)
224 if numpy.iscomplexobj(input):
225 raise TypeError('Complex type not supported')
226 if structure is None:
227 structure = generate_binary_structure(input.ndim, 1)
228 else:
229 structure = numpy.asarray(structure, dtype=bool)
230 if structure.ndim != input.ndim:
231 raise RuntimeError('structure and input must have same dimensionality')
232 if not structure.flags.contiguous:
233 structure = structure.copy()
234 if numpy.prod(structure.shape, axis=0) < 1:
235 raise RuntimeError('structure must not be empty')
236 if mask is not None:
237 mask = numpy.asarray(mask)
238 if mask.shape != input.shape:
239 raise RuntimeError('mask and input must have equal sizes')
240 origin = _ni_support._normalize_sequence(origin, input.ndim)
241 cit = _center_is_true(structure, origin)
242 if isinstance(output, numpy.ndarray):
243 if numpy.iscomplexobj(output):
244 raise TypeError('Complex output type not supported')
245 else:
246 output = bool
247 output = _ni_support._get_output(output, input)
248 temp_needed = numpy.may_share_memory(input, output)
249 if temp_needed:
250 # input and output arrays cannot share memory
251 temp = output
252 output = _ni_support._get_output(output.dtype, input)
253 if iterations == 1:
254 _nd_image.binary_erosion(input, structure, mask, output,
255 border_value, origin, invert, cit, 0)
256 elif cit and not brute_force:
257 changed, coordinate_list = _nd_image.binary_erosion(
258 input, structure, mask, output,
259 border_value, origin, invert, cit, 1)
260 structure = structure[tuple([slice(None, None, -1)] *
261 structure.ndim)]
262 for ii in range(len(origin)):
263 origin[ii] = -origin[ii]
264 if not structure.shape[ii] & 1:
265 origin[ii] -= 1
266 if mask is not None:
267 mask = numpy.asarray(mask, dtype=numpy.int8)
268 if not structure.flags.contiguous:
269 structure = structure.copy()
270 _nd_image.binary_erosion2(output, structure, mask, iterations - 1,
271 origin, invert, coordinate_list)
272 else:
273 tmp_in = numpy.empty_like(input, dtype=bool)
274 tmp_out = output
275 if iterations >= 1 and not iterations & 1:
276 tmp_in, tmp_out = tmp_out, tmp_in
277 changed = _nd_image.binary_erosion(
278 input, structure, mask, tmp_out,
279 border_value, origin, invert, cit, 0)
280 ii = 1
281 while ii < iterations or (iterations < 1 and changed):
282 tmp_in, tmp_out = tmp_out, tmp_in
283 changed = _nd_image.binary_erosion(
284 tmp_in, structure, mask, tmp_out,
285 border_value, origin, invert, cit, 0)
286 ii += 1
287 if temp_needed:
288 temp[...] = output
289 output = temp
290 return output
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 >>> import numpy as np
358 >>> a = np.zeros((7,7), dtype=int)
359 >>> a[1:6, 2:5] = 1
360 >>> a
361 array([[0, 0, 0, 0, 0, 0, 0],
362 [0, 0, 1, 1, 1, 0, 0],
363 [0, 0, 1, 1, 1, 0, 0],
364 [0, 0, 1, 1, 1, 0, 0],
365 [0, 0, 1, 1, 1, 0, 0],
366 [0, 0, 1, 1, 1, 0, 0],
367 [0, 0, 0, 0, 0, 0, 0]])
368 >>> ndimage.binary_erosion(a).astype(a.dtype)
369 array([[0, 0, 0, 0, 0, 0, 0],
370 [0, 0, 0, 0, 0, 0, 0],
371 [0, 0, 0, 1, 0, 0, 0],
372 [0, 0, 0, 1, 0, 0, 0],
373 [0, 0, 0, 1, 0, 0, 0],
374 [0, 0, 0, 0, 0, 0, 0],
375 [0, 0, 0, 0, 0, 0, 0]])
376 >>> #Erosion removes objects smaller than the structure
377 >>> ndimage.binary_erosion(a, structure=np.ones((5,5))).astype(a.dtype)
378 array([[0, 0, 0, 0, 0, 0, 0],
379 [0, 0, 0, 0, 0, 0, 0],
380 [0, 0, 0, 0, 0, 0, 0],
381 [0, 0, 0, 0, 0, 0, 0],
382 [0, 0, 0, 0, 0, 0, 0],
383 [0, 0, 0, 0, 0, 0, 0],
384 [0, 0, 0, 0, 0, 0, 0]])
386 """
387 return _binary_erosion(input, structure, iterations, mask,
388 output, border_value, origin, 0, brute_force)
391def binary_dilation(input, structure=None, iterations=1, mask=None,
392 output=None, border_value=0, origin=0,
393 brute_force=False):
394 """
395 Multidimensional binary dilation with the given structuring element.
397 Parameters
398 ----------
399 input : array_like
400 Binary array_like to be dilated. Non-zero (True) elements form
401 the subset to be dilated.
402 structure : array_like, optional
403 Structuring element used for the dilation. Non-zero elements are
404 considered True. If no structuring element is provided an element
405 is generated with a square connectivity equal to one.
406 iterations : int, optional
407 The dilation is repeated `iterations` times (one, by default).
408 If iterations is less than 1, the dilation is repeated until the
409 result does not change anymore. Only an integer of iterations is
410 accepted.
411 mask : array_like, optional
412 If a mask is given, only those elements with a True value at
413 the corresponding mask element are modified at each iteration.
414 output : ndarray, optional
415 Array of the same shape as input, into which the output is placed.
416 By default, a new array is created.
417 border_value : int (cast to 0 or 1), optional
418 Value at the border in the output array.
419 origin : int or tuple of ints, optional
420 Placement of the filter, by default 0.
421 brute_force : boolean, optional
422 Memory condition: if False, only the pixels whose value was changed in
423 the last iteration are tracked as candidates to be updated (dilated)
424 in the current iteration; if True all pixels are considered as
425 candidates for dilation, regardless of what happened in the previous
426 iteration. False by default.
428 Returns
429 -------
430 binary_dilation : ndarray of bools
431 Dilation of the input by the structuring element.
433 See also
434 --------
435 grey_dilation, binary_erosion, binary_closing, binary_opening,
436 generate_binary_structure
438 Notes
439 -----
440 Dilation [1]_ is a mathematical morphology operation [2]_ that uses a
441 structuring element for expanding the shapes in an image. The binary
442 dilation of an image by a structuring element is the locus of the points
443 covered by the structuring element, when its center lies within the
444 non-zero points of the image.
446 References
447 ----------
448 .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29
449 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
451 Examples
452 --------
453 >>> from scipy import ndimage
454 >>> import numpy as np
455 >>> a = np.zeros((5, 5))
456 >>> a[2, 2] = 1
457 >>> a
458 array([[ 0., 0., 0., 0., 0.],
459 [ 0., 0., 0., 0., 0.],
460 [ 0., 0., 1., 0., 0.],
461 [ 0., 0., 0., 0., 0.],
462 [ 0., 0., 0., 0., 0.]])
463 >>> ndimage.binary_dilation(a)
464 array([[False, False, False, False, False],
465 [False, False, True, False, False],
466 [False, True, True, True, False],
467 [False, False, True, False, False],
468 [False, False, False, False, False]], dtype=bool)
469 >>> ndimage.binary_dilation(a).astype(a.dtype)
470 array([[ 0., 0., 0., 0., 0.],
471 [ 0., 0., 1., 0., 0.],
472 [ 0., 1., 1., 1., 0.],
473 [ 0., 0., 1., 0., 0.],
474 [ 0., 0., 0., 0., 0.]])
475 >>> # 3x3 structuring element with connectivity 1, used by default
476 >>> struct1 = ndimage.generate_binary_structure(2, 1)
477 >>> struct1
478 array([[False, True, False],
479 [ True, True, True],
480 [False, True, False]], dtype=bool)
481 >>> # 3x3 structuring element with connectivity 2
482 >>> struct2 = ndimage.generate_binary_structure(2, 2)
483 >>> struct2
484 array([[ True, True, True],
485 [ True, True, True],
486 [ True, True, True]], dtype=bool)
487 >>> ndimage.binary_dilation(a, structure=struct1).astype(a.dtype)
488 array([[ 0., 0., 0., 0., 0.],
489 [ 0., 0., 1., 0., 0.],
490 [ 0., 1., 1., 1., 0.],
491 [ 0., 0., 1., 0., 0.],
492 [ 0., 0., 0., 0., 0.]])
493 >>> ndimage.binary_dilation(a, structure=struct2).astype(a.dtype)
494 array([[ 0., 0., 0., 0., 0.],
495 [ 0., 1., 1., 1., 0.],
496 [ 0., 1., 1., 1., 0.],
497 [ 0., 1., 1., 1., 0.],
498 [ 0., 0., 0., 0., 0.]])
499 >>> ndimage.binary_dilation(a, structure=struct1,\\
500 ... iterations=2).astype(a.dtype)
501 array([[ 0., 0., 1., 0., 0.],
502 [ 0., 1., 1., 1., 0.],
503 [ 1., 1., 1., 1., 1.],
504 [ 0., 1., 1., 1., 0.],
505 [ 0., 0., 1., 0., 0.]])
507 """
508 input = numpy.asarray(input)
509 if structure is None:
510 structure = generate_binary_structure(input.ndim, 1)
511 origin = _ni_support._normalize_sequence(origin, input.ndim)
512 structure = numpy.asarray(structure)
513 structure = structure[tuple([slice(None, None, -1)] *
514 structure.ndim)]
515 for ii in range(len(origin)):
516 origin[ii] = -origin[ii]
517 if not structure.shape[ii] & 1:
518 origin[ii] -= 1
520 return _binary_erosion(input, structure, iterations, mask,
521 output, border_value, origin, 1, brute_force)
524def binary_opening(input, structure=None, iterations=1, output=None,
525 origin=0, mask=None, border_value=0, brute_force=False):
526 """
527 Multidimensional binary opening with the given structuring element.
529 The *opening* of an input image by a structuring element is the
530 *dilation* of the *erosion* of the image by the structuring element.
532 Parameters
533 ----------
534 input : array_like
535 Binary array_like to be opened. Non-zero (True) elements form
536 the subset to be opened.
537 structure : array_like, optional
538 Structuring element used for the opening. Non-zero elements are
539 considered True. If no structuring element is provided an element
540 is generated with a square connectivity equal to one (i.e., only
541 nearest neighbors are connected to the center, diagonally-connected
542 elements are not considered neighbors).
543 iterations : int, optional
544 The erosion step of the opening, then the dilation step are each
545 repeated `iterations` times (one, by default). If `iterations` is
546 less than 1, each operation is repeated until the result does
547 not change anymore. Only an integer of iterations is accepted.
548 output : ndarray, optional
549 Array of the same shape as input, into which the output is placed.
550 By default, a new array is created.
551 origin : int or tuple of ints, optional
552 Placement of the filter, by default 0.
553 mask : array_like, optional
554 If a mask is given, only those elements with a True value at
555 the corresponding mask element are modified at each iteration.
557 .. versionadded:: 1.1.0
558 border_value : int (cast to 0 or 1), optional
559 Value at the border in the output array.
561 .. versionadded:: 1.1.0
562 brute_force : boolean, optional
563 Memory condition: if False, only the pixels whose value was changed in
564 the last iteration are tracked as candidates to be updated in the
565 current iteration; if true all pixels are considered as candidates for
566 update, regardless of what happened in the previous iteration.
567 False by default.
569 .. versionadded:: 1.1.0
571 Returns
572 -------
573 binary_opening : ndarray of bools
574 Opening of the input by the structuring element.
576 See also
577 --------
578 grey_opening, binary_closing, binary_erosion, binary_dilation,
579 generate_binary_structure
581 Notes
582 -----
583 *Opening* [1]_ is a mathematical morphology operation [2]_ that
584 consists in the succession of an erosion and a dilation of the
585 input with the same structuring element. Opening, therefore, removes
586 objects smaller than the structuring element.
588 Together with *closing* (`binary_closing`), opening can be used for
589 noise removal.
591 References
592 ----------
593 .. [1] https://en.wikipedia.org/wiki/Opening_%28morphology%29
594 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
596 Examples
597 --------
598 >>> from scipy import ndimage
599 >>> import numpy as np
600 >>> a = np.zeros((5,5), dtype=int)
601 >>> a[1:4, 1:4] = 1; a[4, 4] = 1
602 >>> a
603 array([[0, 0, 0, 0, 0],
604 [0, 1, 1, 1, 0],
605 [0, 1, 1, 1, 0],
606 [0, 1, 1, 1, 0],
607 [0, 0, 0, 0, 1]])
608 >>> # Opening removes small objects
609 >>> ndimage.binary_opening(a, structure=np.ones((3,3))).astype(int)
610 array([[0, 0, 0, 0, 0],
611 [0, 1, 1, 1, 0],
612 [0, 1, 1, 1, 0],
613 [0, 1, 1, 1, 0],
614 [0, 0, 0, 0, 0]])
615 >>> # Opening can also smooth corners
616 >>> ndimage.binary_opening(a).astype(int)
617 array([[0, 0, 0, 0, 0],
618 [0, 0, 1, 0, 0],
619 [0, 1, 1, 1, 0],
620 [0, 0, 1, 0, 0],
621 [0, 0, 0, 0, 0]])
622 >>> # Opening is the dilation of the erosion of the input
623 >>> ndimage.binary_erosion(a).astype(int)
624 array([[0, 0, 0, 0, 0],
625 [0, 0, 0, 0, 0],
626 [0, 0, 1, 0, 0],
627 [0, 0, 0, 0, 0],
628 [0, 0, 0, 0, 0]])
629 >>> ndimage.binary_dilation(ndimage.binary_erosion(a)).astype(int)
630 array([[0, 0, 0, 0, 0],
631 [0, 0, 1, 0, 0],
632 [0, 1, 1, 1, 0],
633 [0, 0, 1, 0, 0],
634 [0, 0, 0, 0, 0]])
636 """
637 input = numpy.asarray(input)
638 if structure is None:
639 rank = input.ndim
640 structure = generate_binary_structure(rank, 1)
642 tmp = binary_erosion(input, structure, iterations, mask, None,
643 border_value, origin, brute_force)
644 return binary_dilation(tmp, structure, iterations, mask, output,
645 border_value, origin, brute_force)
648def binary_closing(input, structure=None, iterations=1, output=None,
649 origin=0, mask=None, border_value=0, brute_force=False):
650 """
651 Multidimensional binary closing with the given structuring element.
653 The *closing* of an input image by a structuring element is the
654 *erosion* of the *dilation* of the image by the structuring element.
656 Parameters
657 ----------
658 input : array_like
659 Binary array_like to be closed. Non-zero (True) elements form
660 the subset to be closed.
661 structure : array_like, optional
662 Structuring element used for the closing. Non-zero elements are
663 considered True. If no structuring element is provided an element
664 is generated with a square connectivity equal to one (i.e., only
665 nearest neighbors are connected to the center, diagonally-connected
666 elements are not considered neighbors).
667 iterations : int, optional
668 The dilation step of the closing, then the erosion step are each
669 repeated `iterations` times (one, by default). If iterations is
670 less than 1, each operations is repeated until the result does
671 not change anymore. Only an integer of iterations is accepted.
672 output : ndarray, optional
673 Array of the same shape as input, into which the output is placed.
674 By default, a new array is created.
675 origin : int or tuple of ints, optional
676 Placement of the filter, by default 0.
677 mask : array_like, optional
678 If a mask is given, only those elements with a True value at
679 the corresponding mask element are modified at each iteration.
681 .. versionadded:: 1.1.0
682 border_value : int (cast to 0 or 1), optional
683 Value at the border in the output array.
685 .. versionadded:: 1.1.0
686 brute_force : boolean, optional
687 Memory condition: if False, only the pixels whose value was changed in
688 the last iteration are tracked as candidates to be updated in the
689 current iteration; if true al pixels are considered as candidates for
690 update, regardless of what happened in the previous iteration.
691 False by default.
693 .. versionadded:: 1.1.0
695 Returns
696 -------
697 binary_closing : ndarray of bools
698 Closing of the input by the structuring element.
700 See also
701 --------
702 grey_closing, binary_opening, binary_dilation, binary_erosion,
703 generate_binary_structure
705 Notes
706 -----
707 *Closing* [1]_ is a mathematical morphology operation [2]_ that
708 consists in the succession of a dilation and an erosion of the
709 input with the same structuring element. Closing therefore fills
710 holes smaller than the structuring element.
712 Together with *opening* (`binary_opening`), closing can be used for
713 noise removal.
715 References
716 ----------
717 .. [1] https://en.wikipedia.org/wiki/Closing_%28morphology%29
718 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
720 Examples
721 --------
722 >>> from scipy import ndimage
723 >>> import numpy as np
724 >>> a = np.zeros((5,5), dtype=int)
725 >>> a[1:-1, 1:-1] = 1; a[2,2] = 0
726 >>> a
727 array([[0, 0, 0, 0, 0],
728 [0, 1, 1, 1, 0],
729 [0, 1, 0, 1, 0],
730 [0, 1, 1, 1, 0],
731 [0, 0, 0, 0, 0]])
732 >>> # Closing removes small holes
733 >>> ndimage.binary_closing(a).astype(int)
734 array([[0, 0, 0, 0, 0],
735 [0, 1, 1, 1, 0],
736 [0, 1, 1, 1, 0],
737 [0, 1, 1, 1, 0],
738 [0, 0, 0, 0, 0]])
739 >>> # Closing is the erosion of the dilation of the input
740 >>> ndimage.binary_dilation(a).astype(int)
741 array([[0, 1, 1, 1, 0],
742 [1, 1, 1, 1, 1],
743 [1, 1, 1, 1, 1],
744 [1, 1, 1, 1, 1],
745 [0, 1, 1, 1, 0]])
746 >>> ndimage.binary_erosion(ndimage.binary_dilation(a)).astype(int)
747 array([[0, 0, 0, 0, 0],
748 [0, 1, 1, 1, 0],
749 [0, 1, 1, 1, 0],
750 [0, 1, 1, 1, 0],
751 [0, 0, 0, 0, 0]])
754 >>> a = np.zeros((7,7), dtype=int)
755 >>> a[1:6, 2:5] = 1; a[1:3,3] = 0
756 >>> a
757 array([[0, 0, 0, 0, 0, 0, 0],
758 [0, 0, 1, 0, 1, 0, 0],
759 [0, 0, 1, 0, 1, 0, 0],
760 [0, 0, 1, 1, 1, 0, 0],
761 [0, 0, 1, 1, 1, 0, 0],
762 [0, 0, 1, 1, 1, 0, 0],
763 [0, 0, 0, 0, 0, 0, 0]])
764 >>> # In addition to removing holes, closing can also
765 >>> # coarsen boundaries with fine hollows.
766 >>> ndimage.binary_closing(a).astype(int)
767 array([[0, 0, 0, 0, 0, 0, 0],
768 [0, 0, 1, 0, 1, 0, 0],
769 [0, 0, 1, 1, 1, 0, 0],
770 [0, 0, 1, 1, 1, 0, 0],
771 [0, 0, 1, 1, 1, 0, 0],
772 [0, 0, 1, 1, 1, 0, 0],
773 [0, 0, 0, 0, 0, 0, 0]])
774 >>> ndimage.binary_closing(a, structure=np.ones((2,2))).astype(int)
775 array([[0, 0, 0, 0, 0, 0, 0],
776 [0, 0, 1, 1, 1, 0, 0],
777 [0, 0, 1, 1, 1, 0, 0],
778 [0, 0, 1, 1, 1, 0, 0],
779 [0, 0, 1, 1, 1, 0, 0],
780 [0, 0, 1, 1, 1, 0, 0],
781 [0, 0, 0, 0, 0, 0, 0]])
783 """
784 input = numpy.asarray(input)
785 if structure is None:
786 rank = input.ndim
787 structure = generate_binary_structure(rank, 1)
789 tmp = binary_dilation(input, structure, iterations, mask, None,
790 border_value, origin, brute_force)
791 return binary_erosion(tmp, structure, iterations, mask, output,
792 border_value, origin, brute_force)
795def binary_hit_or_miss(input, structure1=None, structure2=None,
796 output=None, origin1=0, origin2=None):
797 """
798 Multidimensional binary hit-or-miss transform.
800 The hit-or-miss transform finds the locations of a given pattern
801 inside the input image.
803 Parameters
804 ----------
805 input : array_like (cast to booleans)
806 Binary image where a pattern is to be detected.
807 structure1 : array_like (cast to booleans), optional
808 Part of the structuring element to be fitted to the foreground
809 (non-zero elements) of `input`. If no value is provided, a
810 structure of square connectivity 1 is chosen.
811 structure2 : array_like (cast to booleans), optional
812 Second part of the structuring element that has to miss completely
813 the foreground. If no value is provided, the complementary of
814 `structure1` is taken.
815 output : ndarray, optional
816 Array of the same shape as input, into which the output is placed.
817 By default, a new array is created.
818 origin1 : int or tuple of ints, optional
819 Placement of the first part of the structuring element `structure1`,
820 by default 0 for a centered structure.
821 origin2 : int or tuple of ints, optional
822 Placement of the second part of the structuring element `structure2`,
823 by default 0 for a centered structure. If a value is provided for
824 `origin1` and not for `origin2`, then `origin2` is set to `origin1`.
826 Returns
827 -------
828 binary_hit_or_miss : ndarray
829 Hit-or-miss transform of `input` with the given structuring
830 element (`structure1`, `structure2`).
832 See also
833 --------
834 binary_erosion
836 References
837 ----------
838 .. [1] https://en.wikipedia.org/wiki/Hit-or-miss_transform
840 Examples
841 --------
842 >>> from scipy import ndimage
843 >>> import numpy as np
844 >>> a = np.zeros((7,7), dtype=int)
845 >>> a[1, 1] = 1; a[2:4, 2:4] = 1; a[4:6, 4:6] = 1
846 >>> a
847 array([[0, 0, 0, 0, 0, 0, 0],
848 [0, 1, 0, 0, 0, 0, 0],
849 [0, 0, 1, 1, 0, 0, 0],
850 [0, 0, 1, 1, 0, 0, 0],
851 [0, 0, 0, 0, 1, 1, 0],
852 [0, 0, 0, 0, 1, 1, 0],
853 [0, 0, 0, 0, 0, 0, 0]])
854 >>> structure1 = np.array([[1, 0, 0], [0, 1, 1], [0, 1, 1]])
855 >>> structure1
856 array([[1, 0, 0],
857 [0, 1, 1],
858 [0, 1, 1]])
859 >>> # Find the matches of structure1 in the array a
860 >>> ndimage.binary_hit_or_miss(a, structure1=structure1).astype(int)
861 array([[0, 0, 0, 0, 0, 0, 0],
862 [0, 0, 0, 0, 0, 0, 0],
863 [0, 0, 1, 0, 0, 0, 0],
864 [0, 0, 0, 0, 0, 0, 0],
865 [0, 0, 0, 0, 1, 0, 0],
866 [0, 0, 0, 0, 0, 0, 0],
867 [0, 0, 0, 0, 0, 0, 0]])
868 >>> # Change the origin of the filter
869 >>> # origin1=1 is equivalent to origin1=(1,1) here
870 >>> ndimage.binary_hit_or_miss(a, structure1=structure1,\\
871 ... origin1=1).astype(int)
872 array([[0, 0, 0, 0, 0, 0, 0],
873 [0, 0, 0, 0, 0, 0, 0],
874 [0, 0, 0, 0, 0, 0, 0],
875 [0, 0, 0, 1, 0, 0, 0],
876 [0, 0, 0, 0, 0, 0, 0],
877 [0, 0, 0, 0, 0, 1, 0],
878 [0, 0, 0, 0, 0, 0, 0]])
880 """
881 input = numpy.asarray(input)
882 if structure1 is None:
883 structure1 = generate_binary_structure(input.ndim, 1)
884 if structure2 is None:
885 structure2 = numpy.logical_not(structure1)
886 origin1 = _ni_support._normalize_sequence(origin1, input.ndim)
887 if origin2 is None:
888 origin2 = origin1
889 else:
890 origin2 = _ni_support._normalize_sequence(origin2, input.ndim)
892 tmp1 = _binary_erosion(input, structure1, 1, None, None, 0, origin1,
893 0, False)
894 inplace = isinstance(output, numpy.ndarray)
895 result = _binary_erosion(input, structure2, 1, None, output, 0,
896 origin2, 1, False)
897 if inplace:
898 numpy.logical_not(output, output)
899 numpy.logical_and(tmp1, output, output)
900 else:
901 numpy.logical_not(result, result)
902 return numpy.logical_and(tmp1, result)
905def binary_propagation(input, structure=None, mask=None,
906 output=None, border_value=0, origin=0):
907 """
908 Multidimensional binary propagation with the given structuring element.
910 Parameters
911 ----------
912 input : array_like
913 Binary image to be propagated inside `mask`.
914 structure : array_like, optional
915 Structuring element used in the successive dilations. The output
916 may depend on the structuring element, especially if `mask` has
917 several connex components. If no structuring element is
918 provided, an element is generated with a squared connectivity equal
919 to one.
920 mask : array_like, optional
921 Binary mask defining the region into which `input` is allowed to
922 propagate.
923 output : ndarray, optional
924 Array of the same shape as input, into which the output is placed.
925 By default, a new array is created.
926 border_value : int (cast to 0 or 1), optional
927 Value at the border in the output array.
928 origin : int or tuple of ints, optional
929 Placement of the filter, by default 0.
931 Returns
932 -------
933 binary_propagation : ndarray
934 Binary propagation of `input` inside `mask`.
936 Notes
937 -----
938 This function is functionally equivalent to calling binary_dilation
939 with the number of iterations less than one: iterative dilation until
940 the result does not change anymore.
942 The succession of an erosion and propagation inside the original image
943 can be used instead of an *opening* for deleting small objects while
944 keeping the contours of larger objects untouched.
946 References
947 ----------
948 .. [1] http://cmm.ensmp.fr/~serra/cours/pdf/en/ch6en.pdf, slide 15.
949 .. [2] I.T. Young, J.J. Gerbrands, and L.J. van Vliet, "Fundamentals of
950 image processing", 1998
951 ftp://qiftp.tudelft.nl/DIPimage/docs/FIP2.3.pdf
953 Examples
954 --------
955 >>> from scipy import ndimage
956 >>> import numpy as np
957 >>> input = np.zeros((8, 8), dtype=int)
958 >>> input[2, 2] = 1
959 >>> mask = np.zeros((8, 8), dtype=int)
960 >>> mask[1:4, 1:4] = mask[4, 4] = mask[6:8, 6:8] = 1
961 >>> input
962 array([[0, 0, 0, 0, 0, 0, 0, 0],
963 [0, 0, 0, 0, 0, 0, 0, 0],
964 [0, 0, 1, 0, 0, 0, 0, 0],
965 [0, 0, 0, 0, 0, 0, 0, 0],
966 [0, 0, 0, 0, 0, 0, 0, 0],
967 [0, 0, 0, 0, 0, 0, 0, 0],
968 [0, 0, 0, 0, 0, 0, 0, 0],
969 [0, 0, 0, 0, 0, 0, 0, 0]])
970 >>> mask
971 array([[0, 0, 0, 0, 0, 0, 0, 0],
972 [0, 1, 1, 1, 0, 0, 0, 0],
973 [0, 1, 1, 1, 0, 0, 0, 0],
974 [0, 1, 1, 1, 0, 0, 0, 0],
975 [0, 0, 0, 0, 1, 0, 0, 0],
976 [0, 0, 0, 0, 0, 0, 0, 0],
977 [0, 0, 0, 0, 0, 0, 1, 1],
978 [0, 0, 0, 0, 0, 0, 1, 1]])
979 >>> ndimage.binary_propagation(input, mask=mask).astype(int)
980 array([[0, 0, 0, 0, 0, 0, 0, 0],
981 [0, 1, 1, 1, 0, 0, 0, 0],
982 [0, 1, 1, 1, 0, 0, 0, 0],
983 [0, 1, 1, 1, 0, 0, 0, 0],
984 [0, 0, 0, 0, 0, 0, 0, 0],
985 [0, 0, 0, 0, 0, 0, 0, 0],
986 [0, 0, 0, 0, 0, 0, 0, 0],
987 [0, 0, 0, 0, 0, 0, 0, 0]])
988 >>> ndimage.binary_propagation(input, mask=mask,\\
989 ... structure=np.ones((3,3))).astype(int)
990 array([[0, 0, 0, 0, 0, 0, 0, 0],
991 [0, 1, 1, 1, 0, 0, 0, 0],
992 [0, 1, 1, 1, 0, 0, 0, 0],
993 [0, 1, 1, 1, 0, 0, 0, 0],
994 [0, 0, 0, 0, 1, 0, 0, 0],
995 [0, 0, 0, 0, 0, 0, 0, 0],
996 [0, 0, 0, 0, 0, 0, 0, 0],
997 [0, 0, 0, 0, 0, 0, 0, 0]])
999 >>> # Comparison between opening and erosion+propagation
1000 >>> a = np.zeros((6,6), dtype=int)
1001 >>> a[2:5, 2:5] = 1; a[0, 0] = 1; a[5, 5] = 1
1002 >>> a
1003 array([[1, 0, 0, 0, 0, 0],
1004 [0, 0, 0, 0, 0, 0],
1005 [0, 0, 1, 1, 1, 0],
1006 [0, 0, 1, 1, 1, 0],
1007 [0, 0, 1, 1, 1, 0],
1008 [0, 0, 0, 0, 0, 1]])
1009 >>> ndimage.binary_opening(a).astype(int)
1010 array([[0, 0, 0, 0, 0, 0],
1011 [0, 0, 0, 0, 0, 0],
1012 [0, 0, 0, 1, 0, 0],
1013 [0, 0, 1, 1, 1, 0],
1014 [0, 0, 0, 1, 0, 0],
1015 [0, 0, 0, 0, 0, 0]])
1016 >>> b = ndimage.binary_erosion(a)
1017 >>> b.astype(int)
1018 array([[0, 0, 0, 0, 0, 0],
1019 [0, 0, 0, 0, 0, 0],
1020 [0, 0, 0, 0, 0, 0],
1021 [0, 0, 0, 1, 0, 0],
1022 [0, 0, 0, 0, 0, 0],
1023 [0, 0, 0, 0, 0, 0]])
1024 >>> ndimage.binary_propagation(b, mask=a).astype(int)
1025 array([[0, 0, 0, 0, 0, 0],
1026 [0, 0, 0, 0, 0, 0],
1027 [0, 0, 1, 1, 1, 0],
1028 [0, 0, 1, 1, 1, 0],
1029 [0, 0, 1, 1, 1, 0],
1030 [0, 0, 0, 0, 0, 0]])
1032 """
1033 return binary_dilation(input, structure, -1, mask, output,
1034 border_value, origin)
1037def binary_fill_holes(input, structure=None, output=None, origin=0):
1038 """
1039 Fill the holes in binary objects.
1042 Parameters
1043 ----------
1044 input : array_like
1045 N-D binary array with holes to be filled
1046 structure : array_like, optional
1047 Structuring element used in the computation; large-size elements
1048 make computations faster but may miss holes separated from the
1049 background by thin regions. The default element (with a square
1050 connectivity equal to one) yields the intuitive result where all
1051 holes in the input have been filled.
1052 output : ndarray, optional
1053 Array of the same shape as input, into which the output is placed.
1054 By default, a new array is created.
1055 origin : int, tuple of ints, optional
1056 Position of the structuring element.
1058 Returns
1059 -------
1060 out : ndarray
1061 Transformation of the initial image `input` where holes have been
1062 filled.
1064 See also
1065 --------
1066 binary_dilation, binary_propagation, label
1068 Notes
1069 -----
1070 The algorithm used in this function consists in invading the complementary
1071 of the shapes in `input` from the outer boundary of the image,
1072 using binary dilations. Holes are not connected to the boundary and are
1073 therefore not invaded. The result is the complementary subset of the
1074 invaded region.
1076 References
1077 ----------
1078 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
1081 Examples
1082 --------
1083 >>> from scipy import ndimage
1084 >>> import numpy as np
1085 >>> a = np.zeros((5, 5), dtype=int)
1086 >>> a[1:4, 1:4] = 1
1087 >>> a[2,2] = 0
1088 >>> a
1089 array([[0, 0, 0, 0, 0],
1090 [0, 1, 1, 1, 0],
1091 [0, 1, 0, 1, 0],
1092 [0, 1, 1, 1, 0],
1093 [0, 0, 0, 0, 0]])
1094 >>> ndimage.binary_fill_holes(a).astype(int)
1095 array([[0, 0, 0, 0, 0],
1096 [0, 1, 1, 1, 0],
1097 [0, 1, 1, 1, 0],
1098 [0, 1, 1, 1, 0],
1099 [0, 0, 0, 0, 0]])
1100 >>> # Too big structuring element
1101 >>> ndimage.binary_fill_holes(a, structure=np.ones((5,5))).astype(int)
1102 array([[0, 0, 0, 0, 0],
1103 [0, 1, 1, 1, 0],
1104 [0, 1, 0, 1, 0],
1105 [0, 1, 1, 1, 0],
1106 [0, 0, 0, 0, 0]])
1108 """
1109 mask = numpy.logical_not(input)
1110 tmp = numpy.zeros(mask.shape, bool)
1111 inplace = isinstance(output, numpy.ndarray)
1112 if inplace:
1113 binary_dilation(tmp, structure, -1, mask, output, 1, origin)
1114 numpy.logical_not(output, output)
1115 else:
1116 output = binary_dilation(tmp, structure, -1, mask, None, 1,
1117 origin)
1118 numpy.logical_not(output, output)
1119 return output
1122def grey_erosion(input, size=None, footprint=None, structure=None,
1123 output=None, mode="reflect", cval=0.0, origin=0):
1124 """
1125 Calculate a greyscale erosion, using either a structuring element,
1126 or a footprint corresponding to a flat structuring element.
1128 Grayscale erosion is a mathematical morphology operation. For the
1129 simple case of a full and flat structuring element, it can be viewed
1130 as a minimum filter over a sliding window.
1132 Parameters
1133 ----------
1134 input : array_like
1135 Array over which the grayscale erosion is to be computed.
1136 size : tuple of ints
1137 Shape of a flat and full structuring element used for the grayscale
1138 erosion. Optional if `footprint` or `structure` is provided.
1139 footprint : array of ints, optional
1140 Positions of non-infinite elements of a flat structuring element
1141 used for the grayscale erosion. Non-zero values give the set of
1142 neighbors of the center over which the minimum is chosen.
1143 structure : array of ints, optional
1144 Structuring element used for the grayscale erosion. `structure`
1145 may be a non-flat structuring element.
1146 output : array, optional
1147 An array used for storing the output of the erosion may be provided.
1148 mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional
1149 The `mode` parameter determines how the array borders are
1150 handled, where `cval` is the value when mode is equal to
1151 'constant'. Default is 'reflect'
1152 cval : scalar, optional
1153 Value to fill past edges of input if `mode` is 'constant'. Default
1154 is 0.0.
1155 origin : scalar, optional
1156 The `origin` parameter controls the placement of the filter.
1157 Default 0
1159 Returns
1160 -------
1161 output : ndarray
1162 Grayscale erosion of `input`.
1164 See also
1165 --------
1166 binary_erosion, grey_dilation, grey_opening, grey_closing
1167 generate_binary_structure, minimum_filter
1169 Notes
1170 -----
1171 The grayscale erosion of an image input by a structuring element s defined
1172 over a domain E is given by:
1174 (input+s)(x) = min {input(y) - s(x-y), for y in E}
1176 In particular, for structuring elements defined as
1177 s(y) = 0 for y in E, the grayscale erosion computes the minimum of the
1178 input image inside a sliding window defined by E.
1180 Grayscale erosion [1]_ is a *mathematical morphology* operation [2]_.
1182 References
1183 ----------
1184 .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29
1185 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
1187 Examples
1188 --------
1189 >>> from scipy import ndimage
1190 >>> import numpy as np
1191 >>> a = np.zeros((7,7), dtype=int)
1192 >>> a[1:6, 1:6] = 3
1193 >>> a[4,4] = 2; a[2,3] = 1
1194 >>> a
1195 array([[0, 0, 0, 0, 0, 0, 0],
1196 [0, 3, 3, 3, 3, 3, 0],
1197 [0, 3, 3, 1, 3, 3, 0],
1198 [0, 3, 3, 3, 3, 3, 0],
1199 [0, 3, 3, 3, 2, 3, 0],
1200 [0, 3, 3, 3, 3, 3, 0],
1201 [0, 0, 0, 0, 0, 0, 0]])
1202 >>> ndimage.grey_erosion(a, size=(3,3))
1203 array([[0, 0, 0, 0, 0, 0, 0],
1204 [0, 0, 0, 0, 0, 0, 0],
1205 [0, 0, 1, 1, 1, 0, 0],
1206 [0, 0, 1, 1, 1, 0, 0],
1207 [0, 0, 3, 2, 2, 0, 0],
1208 [0, 0, 0, 0, 0, 0, 0],
1209 [0, 0, 0, 0, 0, 0, 0]])
1210 >>> footprint = ndimage.generate_binary_structure(2, 1)
1211 >>> footprint
1212 array([[False, True, False],
1213 [ True, True, True],
1214 [False, True, False]], dtype=bool)
1215 >>> # Diagonally-connected elements are not considered neighbors
1216 >>> ndimage.grey_erosion(a, size=(3,3), footprint=footprint)
1217 array([[0, 0, 0, 0, 0, 0, 0],
1218 [0, 0, 0, 0, 0, 0, 0],
1219 [0, 0, 1, 1, 1, 0, 0],
1220 [0, 0, 3, 1, 2, 0, 0],
1221 [0, 0, 3, 2, 2, 0, 0],
1222 [0, 0, 0, 0, 0, 0, 0],
1223 [0, 0, 0, 0, 0, 0, 0]])
1225 """
1226 if size is None and footprint is None and structure is None:
1227 raise ValueError("size, footprint, or structure must be specified")
1229 return _filters._min_or_max_filter(input, size, footprint, structure,
1230 output, mode, cval, origin, 1)
1233def grey_dilation(input, size=None, footprint=None, structure=None,
1234 output=None, mode="reflect", cval=0.0, origin=0):
1235 """
1236 Calculate a greyscale dilation, using either a structuring element,
1237 or a footprint corresponding to a flat structuring element.
1239 Grayscale dilation is a mathematical morphology operation. For the
1240 simple case of a full and flat structuring element, it can be viewed
1241 as a maximum filter over a sliding window.
1243 Parameters
1244 ----------
1245 input : array_like
1246 Array over which the grayscale dilation is to be computed.
1247 size : tuple of ints
1248 Shape of a flat and full structuring element used for the grayscale
1249 dilation. Optional if `footprint` or `structure` is provided.
1250 footprint : array of ints, optional
1251 Positions of non-infinite elements of a flat structuring element
1252 used for the grayscale dilation. Non-zero values give the set of
1253 neighbors of the center over which the maximum is chosen.
1254 structure : array of ints, optional
1255 Structuring element used for the grayscale dilation. `structure`
1256 may be a non-flat structuring element.
1257 output : array, optional
1258 An array used for storing the output of the dilation may be provided.
1259 mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional
1260 The `mode` parameter determines how the array borders are
1261 handled, where `cval` is the value when mode is equal to
1262 'constant'. Default is 'reflect'
1263 cval : scalar, optional
1264 Value to fill past edges of input if `mode` is 'constant'. Default
1265 is 0.0.
1266 origin : scalar, optional
1267 The `origin` parameter controls the placement of the filter.
1268 Default 0
1270 Returns
1271 -------
1272 grey_dilation : ndarray
1273 Grayscale dilation of `input`.
1275 See also
1276 --------
1277 binary_dilation, grey_erosion, grey_closing, grey_opening
1278 generate_binary_structure, maximum_filter
1280 Notes
1281 -----
1282 The grayscale dilation of an image input by a structuring element s defined
1283 over a domain E is given by:
1285 (input+s)(x) = max {input(y) + s(x-y), for y in E}
1287 In particular, for structuring elements defined as
1288 s(y) = 0 for y in E, the grayscale dilation computes the maximum of the
1289 input image inside a sliding window defined by E.
1291 Grayscale dilation [1]_ is a *mathematical morphology* operation [2]_.
1293 References
1294 ----------
1295 .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29
1296 .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
1298 Examples
1299 --------
1300 >>> from scipy import ndimage
1301 >>> import numpy as np
1302 >>> a = np.zeros((7,7), dtype=int)
1303 >>> a[2:5, 2:5] = 1
1304 >>> a[4,4] = 2; a[2,3] = 3
1305 >>> a
1306 array([[0, 0, 0, 0, 0, 0, 0],
1307 [0, 0, 0, 0, 0, 0, 0],
1308 [0, 0, 1, 3, 1, 0, 0],
1309 [0, 0, 1, 1, 1, 0, 0],
1310 [0, 0, 1, 1, 2, 0, 0],
1311 [0, 0, 0, 0, 0, 0, 0],
1312 [0, 0, 0, 0, 0, 0, 0]])
1313 >>> ndimage.grey_dilation(a, size=(3,3))
1314 array([[0, 0, 0, 0, 0, 0, 0],
1315 [0, 1, 3, 3, 3, 1, 0],
1316 [0, 1, 3, 3, 3, 1, 0],
1317 [0, 1, 3, 3, 3, 2, 0],
1318 [0, 1, 1, 2, 2, 2, 0],
1319 [0, 1, 1, 2, 2, 2, 0],
1320 [0, 0, 0, 0, 0, 0, 0]])
1321 >>> ndimage.grey_dilation(a, footprint=np.ones((3,3)))
1322 array([[0, 0, 0, 0, 0, 0, 0],
1323 [0, 1, 3, 3, 3, 1, 0],
1324 [0, 1, 3, 3, 3, 1, 0],
1325 [0, 1, 3, 3, 3, 2, 0],
1326 [0, 1, 1, 2, 2, 2, 0],
1327 [0, 1, 1, 2, 2, 2, 0],
1328 [0, 0, 0, 0, 0, 0, 0]])
1329 >>> s = ndimage.generate_binary_structure(2,1)
1330 >>> s
1331 array([[False, True, False],
1332 [ True, True, True],
1333 [False, True, False]], dtype=bool)
1334 >>> ndimage.grey_dilation(a, footprint=s)
1335 array([[0, 0, 0, 0, 0, 0, 0],
1336 [0, 0, 1, 3, 1, 0, 0],
1337 [0, 1, 3, 3, 3, 1, 0],
1338 [0, 1, 1, 3, 2, 1, 0],
1339 [0, 1, 1, 2, 2, 2, 0],
1340 [0, 0, 1, 1, 2, 0, 0],
1341 [0, 0, 0, 0, 0, 0, 0]])
1342 >>> ndimage.grey_dilation(a, size=(3,3), structure=np.ones((3,3)))
1343 array([[1, 1, 1, 1, 1, 1, 1],
1344 [1, 2, 4, 4, 4, 2, 1],
1345 [1, 2, 4, 4, 4, 2, 1],
1346 [1, 2, 4, 4, 4, 3, 1],
1347 [1, 2, 2, 3, 3, 3, 1],
1348 [1, 2, 2, 3, 3, 3, 1],
1349 [1, 1, 1, 1, 1, 1, 1]])
1351 """
1352 if size is None and footprint is None and structure is None:
1353 raise ValueError("size, footprint, or structure must be specified")
1354 if structure is not None:
1355 structure = numpy.asarray(structure)
1356 structure = structure[tuple([slice(None, None, -1)] *
1357 structure.ndim)]
1358 if footprint is not None:
1359 footprint = numpy.asarray(footprint)
1360 footprint = footprint[tuple([slice(None, None, -1)] *
1361 footprint.ndim)]
1363 input = numpy.asarray(input)
1364 origin = _ni_support._normalize_sequence(origin, input.ndim)
1365 for ii in range(len(origin)):
1366 origin[ii] = -origin[ii]
1367 if footprint is not None:
1368 sz = footprint.shape[ii]
1369 elif structure is not None:
1370 sz = structure.shape[ii]
1371 elif numpy.isscalar(size):
1372 sz = size
1373 else:
1374 sz = size[ii]
1375 if not sz & 1:
1376 origin[ii] -= 1
1378 return _filters._min_or_max_filter(input, size, footprint, structure,
1379 output, mode, cval, origin, 0)
1382def grey_opening(input, size=None, footprint=None, structure=None,
1383 output=None, mode="reflect", cval=0.0, origin=0):
1384 """
1385 Multidimensional grayscale opening.
1387 A grayscale opening consists in the succession of a grayscale erosion,
1388 and a grayscale dilation.
1390 Parameters
1391 ----------
1392 input : array_like
1393 Array over which the grayscale opening is to be computed.
1394 size : tuple of ints
1395 Shape of a flat and full structuring element used for the grayscale
1396 opening. Optional if `footprint` or `structure` is provided.
1397 footprint : array of ints, optional
1398 Positions of non-infinite elements of a flat structuring element
1399 used for the grayscale opening.
1400 structure : array of ints, optional
1401 Structuring element used for the grayscale opening. `structure`
1402 may be a non-flat structuring element.
1403 output : array, optional
1404 An array used for storing the output of the opening may be provided.
1405 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
1406 The `mode` parameter determines how the array borders are
1407 handled, where `cval` is the value when mode is equal to
1408 'constant'. Default is 'reflect'
1409 cval : scalar, optional
1410 Value to fill past edges of input if `mode` is 'constant'. Default
1411 is 0.0.
1412 origin : scalar, optional
1413 The `origin` parameter controls the placement of the filter.
1414 Default 0
1416 Returns
1417 -------
1418 grey_opening : ndarray
1419 Result of the grayscale opening of `input` with `structure`.
1421 See also
1422 --------
1423 binary_opening, grey_dilation, grey_erosion, grey_closing
1424 generate_binary_structure
1426 Notes
1427 -----
1428 The action of a grayscale opening with a flat structuring element amounts
1429 to smoothen high local maxima, whereas binary opening erases small objects.
1431 References
1432 ----------
1433 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
1435 Examples
1436 --------
1437 >>> from scipy import ndimage
1438 >>> import numpy as np
1439 >>> a = np.arange(36).reshape((6,6))
1440 >>> a[3, 3] = 50
1441 >>> a
1442 array([[ 0, 1, 2, 3, 4, 5],
1443 [ 6, 7, 8, 9, 10, 11],
1444 [12, 13, 14, 15, 16, 17],
1445 [18, 19, 20, 50, 22, 23],
1446 [24, 25, 26, 27, 28, 29],
1447 [30, 31, 32, 33, 34, 35]])
1448 >>> ndimage.grey_opening(a, size=(3,3))
1449 array([[ 0, 1, 2, 3, 4, 4],
1450 [ 6, 7, 8, 9, 10, 10],
1451 [12, 13, 14, 15, 16, 16],
1452 [18, 19, 20, 22, 22, 22],
1453 [24, 25, 26, 27, 28, 28],
1454 [24, 25, 26, 27, 28, 28]])
1455 >>> # Note that the local maximum a[3,3] has disappeared
1457 """
1458 if (size is not None) and (footprint is not None):
1459 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
1460 tmp = grey_erosion(input, size, footprint, structure, None, mode,
1461 cval, origin)
1462 return grey_dilation(tmp, size, footprint, structure, output, mode,
1463 cval, origin)
1466def grey_closing(input, size=None, footprint=None, structure=None,
1467 output=None, mode="reflect", cval=0.0, origin=0):
1468 """
1469 Multidimensional grayscale closing.
1471 A grayscale closing consists in the succession of a grayscale dilation,
1472 and a grayscale erosion.
1474 Parameters
1475 ----------
1476 input : array_like
1477 Array over which the grayscale closing is to be computed.
1478 size : tuple of ints
1479 Shape of a flat and full structuring element used for the grayscale
1480 closing. Optional if `footprint` or `structure` is provided.
1481 footprint : array of ints, optional
1482 Positions of non-infinite elements of a flat structuring element
1483 used for the grayscale closing.
1484 structure : array of ints, optional
1485 Structuring element used for the grayscale closing. `structure`
1486 may be a non-flat structuring element.
1487 output : array, optional
1488 An array used for storing the output of the closing may be provided.
1489 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
1490 The `mode` parameter determines how the array borders are
1491 handled, where `cval` is the value when mode is equal to
1492 'constant'. Default is 'reflect'
1493 cval : scalar, optional
1494 Value to fill past edges of input if `mode` is 'constant'. Default
1495 is 0.0.
1496 origin : scalar, optional
1497 The `origin` parameter controls the placement of the filter.
1498 Default 0
1500 Returns
1501 -------
1502 grey_closing : ndarray
1503 Result of the grayscale closing of `input` with `structure`.
1505 See also
1506 --------
1507 binary_closing, grey_dilation, grey_erosion, grey_opening,
1508 generate_binary_structure
1510 Notes
1511 -----
1512 The action of a grayscale closing with a flat structuring element amounts
1513 to smoothen deep local minima, whereas binary closing fills small holes.
1515 References
1516 ----------
1517 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
1519 Examples
1520 --------
1521 >>> from scipy import ndimage
1522 >>> import numpy as np
1523 >>> a = np.arange(36).reshape((6,6))
1524 >>> a[3,3] = 0
1525 >>> a
1526 array([[ 0, 1, 2, 3, 4, 5],
1527 [ 6, 7, 8, 9, 10, 11],
1528 [12, 13, 14, 15, 16, 17],
1529 [18, 19, 20, 0, 22, 23],
1530 [24, 25, 26, 27, 28, 29],
1531 [30, 31, 32, 33, 34, 35]])
1532 >>> ndimage.grey_closing(a, size=(3,3))
1533 array([[ 7, 7, 8, 9, 10, 11],
1534 [ 7, 7, 8, 9, 10, 11],
1535 [13, 13, 14, 15, 16, 17],
1536 [19, 19, 20, 20, 22, 23],
1537 [25, 25, 26, 27, 28, 29],
1538 [31, 31, 32, 33, 34, 35]])
1539 >>> # Note that the local minimum a[3,3] has disappeared
1541 """
1542 if (size is not None) and (footprint is not None):
1543 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
1544 tmp = grey_dilation(input, size, footprint, structure, None, mode,
1545 cval, origin)
1546 return grey_erosion(tmp, size, footprint, structure, output, mode,
1547 cval, origin)
1550def morphological_gradient(input, size=None, footprint=None, structure=None,
1551 output=None, mode="reflect", cval=0.0, origin=0):
1552 """
1553 Multidimensional morphological gradient.
1555 The morphological gradient is calculated as the difference between a
1556 dilation and an erosion of the input with a given structuring element.
1558 Parameters
1559 ----------
1560 input : array_like
1561 Array over which to compute the morphlogical gradient.
1562 size : tuple of ints
1563 Shape of a flat and full structuring element used for the mathematical
1564 morphology operations. Optional if `footprint` or `structure` is
1565 provided. A larger `size` yields a more blurred gradient.
1566 footprint : array of ints, optional
1567 Positions of non-infinite elements of a flat structuring element
1568 used for the morphology operations. Larger footprints
1569 give a more blurred morphological gradient.
1570 structure : array of ints, optional
1571 Structuring element used for the morphology operations.
1572 `structure` may be a non-flat structuring element.
1573 output : array, optional
1574 An array used for storing the output of the morphological gradient
1575 may be provided.
1576 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
1577 The `mode` parameter determines how the array borders are
1578 handled, where `cval` is the value when mode is equal to
1579 'constant'. Default is 'reflect'
1580 cval : scalar, optional
1581 Value to fill past edges of input if `mode` is 'constant'. Default
1582 is 0.0.
1583 origin : scalar, optional
1584 The `origin` parameter controls the placement of the filter.
1585 Default 0
1587 Returns
1588 -------
1589 morphological_gradient : ndarray
1590 Morphological gradient of `input`.
1592 See also
1593 --------
1594 grey_dilation, grey_erosion, gaussian_gradient_magnitude
1596 Notes
1597 -----
1598 For a flat structuring element, the morphological gradient
1599 computed at a given point corresponds to the maximal difference
1600 between elements of the input among the elements covered by the
1601 structuring element centered on the point.
1603 References
1604 ----------
1605 .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
1607 Examples
1608 --------
1609 >>> from scipy import ndimage
1610 >>> import numpy as np
1611 >>> a = np.zeros((7,7), dtype=int)
1612 >>> a[2:5, 2:5] = 1
1613 >>> ndimage.morphological_gradient(a, size=(3,3))
1614 array([[0, 0, 0, 0, 0, 0, 0],
1615 [0, 1, 1, 1, 1, 1, 0],
1616 [0, 1, 1, 1, 1, 1, 0],
1617 [0, 1, 1, 0, 1, 1, 0],
1618 [0, 1, 1, 1, 1, 1, 0],
1619 [0, 1, 1, 1, 1, 1, 0],
1620 [0, 0, 0, 0, 0, 0, 0]])
1621 >>> # The morphological gradient is computed as the difference
1622 >>> # between a dilation and an erosion
1623 >>> ndimage.grey_dilation(a, size=(3,3)) -\\
1624 ... ndimage.grey_erosion(a, size=(3,3))
1625 array([[0, 0, 0, 0, 0, 0, 0],
1626 [0, 1, 1, 1, 1, 1, 0],
1627 [0, 1, 1, 1, 1, 1, 0],
1628 [0, 1, 1, 0, 1, 1, 0],
1629 [0, 1, 1, 1, 1, 1, 0],
1630 [0, 1, 1, 1, 1, 1, 0],
1631 [0, 0, 0, 0, 0, 0, 0]])
1632 >>> a = np.zeros((7,7), dtype=int)
1633 >>> a[2:5, 2:5] = 1
1634 >>> a[4,4] = 2; a[2,3] = 3
1635 >>> a
1636 array([[0, 0, 0, 0, 0, 0, 0],
1637 [0, 0, 0, 0, 0, 0, 0],
1638 [0, 0, 1, 3, 1, 0, 0],
1639 [0, 0, 1, 1, 1, 0, 0],
1640 [0, 0, 1, 1, 2, 0, 0],
1641 [0, 0, 0, 0, 0, 0, 0],
1642 [0, 0, 0, 0, 0, 0, 0]])
1643 >>> ndimage.morphological_gradient(a, size=(3,3))
1644 array([[0, 0, 0, 0, 0, 0, 0],
1645 [0, 1, 3, 3, 3, 1, 0],
1646 [0, 1, 3, 3, 3, 1, 0],
1647 [0, 1, 3, 2, 3, 2, 0],
1648 [0, 1, 1, 2, 2, 2, 0],
1649 [0, 1, 1, 2, 2, 2, 0],
1650 [0, 0, 0, 0, 0, 0, 0]])
1652 """
1653 tmp = grey_dilation(input, size, footprint, structure, None, mode,
1654 cval, origin)
1655 if isinstance(output, numpy.ndarray):
1656 grey_erosion(input, size, footprint, structure, output, mode,
1657 cval, origin)
1658 return numpy.subtract(tmp, output, output)
1659 else:
1660 return (tmp - grey_erosion(input, size, footprint, structure,
1661 None, mode, cval, origin))
1664def morphological_laplace(input, size=None, footprint=None,
1665 structure=None, output=None,
1666 mode="reflect", cval=0.0, origin=0):
1667 """
1668 Multidimensional morphological laplace.
1670 Parameters
1671 ----------
1672 input : array_like
1673 Input.
1674 size : int or sequence of ints, optional
1675 See `structure`.
1676 footprint : bool or ndarray, optional
1677 See `structure`.
1678 structure : structure, optional
1679 Either `size`, `footprint`, or the `structure` must be provided.
1680 output : ndarray, optional
1681 An output array can optionally be provided.
1682 mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional
1683 The mode parameter determines how the array borders are handled.
1684 For 'constant' mode, values beyond borders are set to be `cval`.
1685 Default is 'reflect'.
1686 cval : scalar, optional
1687 Value to fill past edges of input if mode is 'constant'.
1688 Default is 0.0
1689 origin : origin, optional
1690 The origin parameter controls the placement of the filter.
1692 Returns
1693 -------
1694 morphological_laplace : ndarray
1695 Output
1697 """
1698 tmp1 = grey_dilation(input, size, footprint, structure, None, mode,
1699 cval, origin)
1700 if isinstance(output, numpy.ndarray):
1701 grey_erosion(input, size, footprint, structure, output, mode,
1702 cval, origin)
1703 numpy.add(tmp1, output, output)
1704 numpy.subtract(output, input, output)
1705 return numpy.subtract(output, input, output)
1706 else:
1707 tmp2 = grey_erosion(input, size, footprint, structure, None, mode,
1708 cval, origin)
1709 numpy.add(tmp1, tmp2, tmp2)
1710 numpy.subtract(tmp2, input, tmp2)
1711 numpy.subtract(tmp2, input, tmp2)
1712 return tmp2
1715def white_tophat(input, size=None, footprint=None, structure=None,
1716 output=None, mode="reflect", cval=0.0, origin=0):
1717 """
1718 Multidimensional white tophat filter.
1720 Parameters
1721 ----------
1722 input : array_like
1723 Input.
1724 size : tuple of ints
1725 Shape of a flat and full structuring element used for the filter.
1726 Optional if `footprint` or `structure` is provided.
1727 footprint : array of ints, optional
1728 Positions of elements of a flat structuring element
1729 used for the white tophat filter.
1730 structure : array of ints, optional
1731 Structuring element used for the filter. `structure`
1732 may be a non-flat structuring element.
1733 output : array, optional
1734 An array used for storing the output of the filter may be provided.
1735 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
1736 The `mode` parameter determines how the array borders are
1737 handled, where `cval` is the value when mode is equal to
1738 'constant'. Default is 'reflect'
1739 cval : scalar, optional
1740 Value to fill past edges of input if `mode` is 'constant'.
1741 Default is 0.0.
1742 origin : scalar, optional
1743 The `origin` parameter controls the placement of the filter.
1744 Default is 0.
1746 Returns
1747 -------
1748 output : ndarray
1749 Result of the filter of `input` with `structure`.
1751 Examples
1752 --------
1753 Subtract gray background from a bright peak.
1755 >>> from scipy.ndimage import generate_binary_structure, white_tophat
1756 >>> import numpy as np
1757 >>> square = generate_binary_structure(rank=2, connectivity=3)
1758 >>> bright_on_gray = np.array([[2, 3, 3, 3, 2],
1759 ... [3, 4, 5, 4, 3],
1760 ... [3, 5, 9, 5, 3],
1761 ... [3, 4, 5, 4, 3],
1762 ... [2, 3, 3, 3, 2]])
1763 >>> white_tophat(input=bright_on_gray, structure=square)
1764 array([[0, 0, 0, 0, 0],
1765 [0, 0, 1, 0, 0],
1766 [0, 1, 5, 1, 0],
1767 [0, 0, 1, 0, 0],
1768 [0, 0, 0, 0, 0]])
1770 See also
1771 --------
1772 black_tophat
1774 """
1775 if (size is not None) and (footprint is not None):
1776 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
1777 tmp = grey_erosion(input, size, footprint, structure, None, mode,
1778 cval, origin)
1779 tmp = grey_dilation(tmp, size, footprint, structure, output, mode,
1780 cval, origin)
1781 if tmp is None:
1782 tmp = output
1784 if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_:
1785 numpy.bitwise_xor(input, tmp, out=tmp)
1786 else:
1787 numpy.subtract(input, tmp, out=tmp)
1788 return tmp
1791def black_tophat(input, size=None, footprint=None,
1792 structure=None, output=None, mode="reflect",
1793 cval=0.0, origin=0):
1794 """
1795 Multidimensional black tophat filter.
1797 Parameters
1798 ----------
1799 input : array_like
1800 Input.
1801 size : tuple of ints, optional
1802 Shape of a flat and full structuring element used for the filter.
1803 Optional if `footprint` or `structure` is provided.
1804 footprint : array of ints, optional
1805 Positions of non-infinite elements of a flat structuring element
1806 used for the black tophat filter.
1807 structure : array of ints, optional
1808 Structuring element used for the filter. `structure`
1809 may be a non-flat structuring element.
1810 output : array, optional
1811 An array used for storing the output of the filter may be provided.
1812 mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
1813 The `mode` parameter determines how the array borders are
1814 handled, where `cval` is the value when mode is equal to
1815 'constant'. Default is 'reflect'
1816 cval : scalar, optional
1817 Value to fill past edges of input if `mode` is 'constant'. Default
1818 is 0.0.
1819 origin : scalar, optional
1820 The `origin` parameter controls the placement of the filter.
1821 Default 0
1823 Returns
1824 -------
1825 black_tophat : ndarray
1826 Result of the filter of `input` with `structure`.
1828 Examples
1829 --------
1830 Change dark peak to bright peak and subtract background.
1832 >>> from scipy.ndimage import generate_binary_structure, black_tophat
1833 >>> import numpy as np
1834 >>> square = generate_binary_structure(rank=2, connectivity=3)
1835 >>> dark_on_gray = np.array([[7, 6, 6, 6, 7],
1836 ... [6, 5, 4, 5, 6],
1837 ... [6, 4, 0, 4, 6],
1838 ... [6, 5, 4, 5, 6],
1839 ... [7, 6, 6, 6, 7]])
1840 >>> black_tophat(input=dark_on_gray, structure=square)
1841 array([[0, 0, 0, 0, 0],
1842 [0, 0, 1, 0, 0],
1843 [0, 1, 5, 1, 0],
1844 [0, 0, 1, 0, 0],
1845 [0, 0, 0, 0, 0]])
1847 See also
1848 --------
1849 white_tophat, grey_opening, grey_closing
1851 """
1852 if (size is not None) and (footprint is not None):
1853 warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
1854 tmp = grey_dilation(input, size, footprint, structure, None, mode,
1855 cval, origin)
1856 tmp = grey_erosion(tmp, size, footprint, structure, output, mode,
1857 cval, origin)
1858 if tmp is None:
1859 tmp = output
1861 if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_:
1862 numpy.bitwise_xor(tmp, input, out=tmp)
1863 else:
1864 numpy.subtract(tmp, input, out=tmp)
1865 return tmp
1868def distance_transform_bf(input, metric="euclidean", sampling=None,
1869 return_distances=True, return_indices=False,
1870 distances=None, indices=None):
1871 """
1872 Distance transform function by a brute force algorithm.
1874 This function calculates the distance transform of the `input`, by
1875 replacing each foreground (non-zero) element, with its
1876 shortest distance to the background (any zero-valued element).
1878 In addition to the distance transform, the feature transform can
1879 be calculated. In this case the index of the closest background
1880 element to each foreground element is returned in a separate array.
1882 Parameters
1883 ----------
1884 input : array_like
1885 Input
1886 metric : {'euclidean', 'taxicab', 'chessboard'}, optional
1887 'cityblock' and 'manhattan' are also valid, and map to 'taxicab'.
1888 The default is 'euclidean'.
1889 sampling : float, or sequence of float, optional
1890 This parameter is only used when `metric` is 'euclidean'.
1891 Spacing of elements along each dimension. If a sequence, must be of
1892 length equal to the input rank; if a single number, this is used for
1893 all axes. If not specified, a grid spacing of unity is implied.
1894 return_distances : bool, optional
1895 Whether to calculate the distance transform.
1896 Default is True.
1897 return_indices : bool, optional
1898 Whether to calculate the feature transform.
1899 Default is False.
1900 distances : ndarray, optional
1901 An output array to store the calculated distance transform, instead of
1902 returning it.
1903 `return_distances` must be True.
1904 It must be the same shape as `input`, and of type float64 if `metric`
1905 is 'euclidean', uint32 otherwise.
1906 indices : int32 ndarray, optional
1907 An output array to store the calculated feature transform, instead of
1908 returning it.
1909 `return_indicies` must be True.
1910 Its shape must be `(input.ndim,) + input.shape`.
1912 Returns
1913 -------
1914 distances : ndarray, optional
1915 The calculated distance transform. Returned only when
1916 `return_distances` is True and `distances` is not supplied.
1917 It will have the same shape as the input array.
1918 indices : int32 ndarray, optional
1919 The calculated feature transform. It has an input-shaped array for each
1920 dimension of the input. See distance_transform_edt documentation for an
1921 example.
1922 Returned only when `return_indices` is True and `indices` is not
1923 supplied.
1925 Notes
1926 -----
1927 This function employs a slow brute force algorithm, see also the
1928 function distance_transform_cdt for more efficient taxicab and
1929 chessboard algorithms.
1931 """
1932 ft_inplace = isinstance(indices, numpy.ndarray)
1933 dt_inplace = isinstance(distances, numpy.ndarray)
1934 _distance_tranform_arg_check(
1935 dt_inplace, ft_inplace, return_distances, return_indices
1936 )
1938 tmp1 = numpy.asarray(input) != 0
1939 struct = generate_binary_structure(tmp1.ndim, tmp1.ndim)
1940 tmp2 = binary_dilation(tmp1, struct)
1941 tmp2 = numpy.logical_xor(tmp1, tmp2)
1942 tmp1 = tmp1.astype(numpy.int8) - tmp2.astype(numpy.int8)
1943 metric = metric.lower()
1944 if metric == 'euclidean':
1945 metric = 1
1946 elif metric in ['taxicab', 'cityblock', 'manhattan']:
1947 metric = 2
1948 elif metric == 'chessboard':
1949 metric = 3
1950 else:
1951 raise RuntimeError('distance metric not supported')
1952 if sampling is not None:
1953 sampling = _ni_support._normalize_sequence(sampling, tmp1.ndim)
1954 sampling = numpy.asarray(sampling, dtype=numpy.float64)
1955 if not sampling.flags.contiguous:
1956 sampling = sampling.copy()
1957 if return_indices:
1958 ft = numpy.zeros(tmp1.shape, dtype=numpy.int32)
1959 else:
1960 ft = None
1961 if return_distances:
1962 if distances is None:
1963 if metric == 1:
1964 dt = numpy.zeros(tmp1.shape, dtype=numpy.float64)
1965 else:
1966 dt = numpy.zeros(tmp1.shape, dtype=numpy.uint32)
1967 else:
1968 if distances.shape != tmp1.shape:
1969 raise RuntimeError('distances array has wrong shape')
1970 if metric == 1:
1971 if distances.dtype.type != numpy.float64:
1972 raise RuntimeError('distances array must be float64')
1973 else:
1974 if distances.dtype.type != numpy.uint32:
1975 raise RuntimeError('distances array must be uint32')
1976 dt = distances
1977 else:
1978 dt = None
1980 _nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft)
1981 if return_indices:
1982 if isinstance(indices, numpy.ndarray):
1983 if indices.dtype.type != numpy.int32:
1984 raise RuntimeError('indices array must be int32')
1985 if indices.shape != (tmp1.ndim,) + tmp1.shape:
1986 raise RuntimeError('indices array has wrong shape')
1987 tmp2 = indices
1988 else:
1989 tmp2 = numpy.indices(tmp1.shape, dtype=numpy.int32)
1990 ft = numpy.ravel(ft)
1991 for ii in range(tmp2.shape[0]):
1992 rtmp = numpy.ravel(tmp2[ii, ...])[ft]
1993 rtmp.shape = tmp1.shape
1994 tmp2[ii, ...] = rtmp
1995 ft = tmp2
1997 # construct and return the result
1998 result = []
1999 if return_distances and not dt_inplace:
2000 result.append(dt)
2001 if return_indices and not ft_inplace:
2002 result.append(ft)
2004 if len(result) == 2:
2005 return tuple(result)
2006 elif len(result) == 1:
2007 return result[0]
2008 else:
2009 return None
2012def distance_transform_cdt(input, metric='chessboard', return_distances=True,
2013 return_indices=False, distances=None, indices=None):
2014 """
2015 Distance transform for chamfer type of transforms.
2017 In addition to the distance transform, the feature transform can
2018 be calculated. In this case the index of the closest background
2019 element to each foreground element is returned in a separate array.
2021 Parameters
2022 ----------
2023 input : array_like
2024 Input
2025 metric : {'chessboard', 'taxicab'} or array_like, optional
2026 The `metric` determines the type of chamfering that is done. If the
2027 `metric` is equal to 'taxicab' a structure is generated using
2028 generate_binary_structure with a squared distance equal to 1. If
2029 the `metric` is equal to 'chessboard', a `metric` is generated
2030 using generate_binary_structure with a squared distance equal to
2031 the dimensionality of the array. These choices correspond to the
2032 common interpretations of the 'taxicab' and the 'chessboard'
2033 distance metrics in two dimensions.
2034 A custom metric may be provided, in the form of a matrix where
2035 each dimension has a length of three.
2036 'cityblock' and 'manhattan' are also valid, and map to 'taxicab'.
2037 The default is 'chessboard'.
2038 return_distances : bool, optional
2039 Whether to calculate the distance transform.
2040 Default is True.
2041 return_indices : bool, optional
2042 Whether to calculate the feature transform.
2043 Default is False.
2044 distances : int32 ndarray, optional
2045 An output array to store the calculated distance transform, instead of
2046 returning it.
2047 `return_distances` must be True.
2048 It must be the same shape as `input`.
2049 indices : int32 ndarray, optional
2050 An output array to store the calculated feature transform, instead of
2051 returning it.
2052 `return_indicies` must be True.
2053 Its shape must be `(input.ndim,) + input.shape`.
2055 Returns
2056 -------
2057 distances : int32 ndarray, optional
2058 The calculated distance transform. Returned only when
2059 `return_distances` is True, and `distances` is not supplied.
2060 It will have the same shape as the input array.
2061 indices : int32 ndarray, optional
2062 The calculated feature transform. It has an input-shaped array for each
2063 dimension of the input. See distance_transform_edt documentation for an
2064 example.
2065 Returned only when `return_indices` is True, and `indices` is not
2066 supplied.
2068 """
2069 ft_inplace = isinstance(indices, numpy.ndarray)
2070 dt_inplace = isinstance(distances, numpy.ndarray)
2071 _distance_tranform_arg_check(
2072 dt_inplace, ft_inplace, return_distances, return_indices
2073 )
2074 input = numpy.asarray(input)
2075 if metric in ['taxicab', 'cityblock', 'manhattan']:
2076 rank = input.ndim
2077 metric = generate_binary_structure(rank, 1)
2078 elif metric == 'chessboard':
2079 rank = input.ndim
2080 metric = generate_binary_structure(rank, rank)
2081 else:
2082 try:
2083 metric = numpy.asarray(metric)
2084 except Exception as e:
2085 raise RuntimeError('invalid metric provided') from e
2086 for s in metric.shape:
2087 if s != 3:
2088 raise RuntimeError('metric sizes must be equal to 3')
2090 if not metric.flags.contiguous:
2091 metric = metric.copy()
2092 if dt_inplace:
2093 if distances.dtype.type != numpy.int32:
2094 raise RuntimeError('distances must be of int32 type')
2095 if distances.shape != input.shape:
2096 raise RuntimeError('distances has wrong shape')
2097 dt = distances
2098 dt[...] = numpy.where(input, -1, 0).astype(numpy.int32)
2099 else:
2100 dt = numpy.where(input, -1, 0).astype(numpy.int32)
2102 rank = dt.ndim
2103 if return_indices:
2104 sz = numpy.prod(dt.shape, axis=0)
2105 ft = numpy.arange(sz, dtype=numpy.int32)
2106 ft.shape = dt.shape
2107 else:
2108 ft = None
2110 _nd_image.distance_transform_op(metric, dt, ft)
2111 dt = dt[tuple([slice(None, None, -1)] * rank)]
2112 if return_indices:
2113 ft = ft[tuple([slice(None, None, -1)] * rank)]
2114 _nd_image.distance_transform_op(metric, dt, ft)
2115 dt = dt[tuple([slice(None, None, -1)] * rank)]
2116 if return_indices:
2117 ft = ft[tuple([slice(None, None, -1)] * rank)]
2118 ft = numpy.ravel(ft)
2119 if ft_inplace:
2120 if indices.dtype.type != numpy.int32:
2121 raise RuntimeError('indices array must be int32')
2122 if indices.shape != (dt.ndim,) + dt.shape:
2123 raise RuntimeError('indices array has wrong shape')
2124 tmp = indices
2125 else:
2126 tmp = numpy.indices(dt.shape, dtype=numpy.int32)
2127 for ii in range(tmp.shape[0]):
2128 rtmp = numpy.ravel(tmp[ii, ...])[ft]
2129 rtmp.shape = dt.shape
2130 tmp[ii, ...] = rtmp
2131 ft = tmp
2133 # construct and return the result
2134 result = []
2135 if return_distances and not dt_inplace:
2136 result.append(dt)
2137 if return_indices and not ft_inplace:
2138 result.append(ft)
2140 if len(result) == 2:
2141 return tuple(result)
2142 elif len(result) == 1:
2143 return result[0]
2144 else:
2145 return None
2148def distance_transform_edt(input, sampling=None, return_distances=True,
2149 return_indices=False, distances=None, indices=None):
2150 """
2151 Exact Euclidean distance transform.
2153 In addition to the distance transform, the feature transform can
2154 be calculated. In this case the index of the closest background
2155 element to each foreground element is returned in a separate array.
2157 Parameters
2158 ----------
2159 input : array_like
2160 Input data to transform. Can be any type but will be converted
2161 into binary: 1 wherever input equates to True, 0 elsewhere.
2162 sampling : float, or sequence of float, optional
2163 Spacing of elements along each dimension. If a sequence, must be of
2164 length equal to the input rank; if a single number, this is used for
2165 all axes. If not specified, a grid spacing of unity is implied.
2166 return_distances : bool, optional
2167 Whether to calculate the distance transform.
2168 Default is True.
2169 return_indices : bool, optional
2170 Whether to calculate the feature transform.
2171 Default is False.
2172 distances : float64 ndarray, optional
2173 An output array to store the calculated distance transform, instead of
2174 returning it.
2175 `return_distances` must be True.
2176 It must be the same shape as `input`.
2177 indices : int32 ndarray, optional
2178 An output array to store the calculated feature transform, instead of
2179 returning it.
2180 `return_indicies` must be True.
2181 Its shape must be `(input.ndim,) + input.shape`.
2183 Returns
2184 -------
2185 distances : float64 ndarray, optional
2186 The calculated distance transform. Returned only when
2187 `return_distances` is True and `distances` is not supplied.
2188 It will have the same shape as the input array.
2189 indices : int32 ndarray, optional
2190 The calculated feature transform. It has an input-shaped array for each
2191 dimension of the input. See example below.
2192 Returned only when `return_indices` is True and `indices` is not
2193 supplied.
2195 Notes
2196 -----
2197 The Euclidean distance transform gives values of the Euclidean
2198 distance::
2200 n
2201 y_i = sqrt(sum (x[i]-b[i])**2)
2202 i
2204 where b[i] is the background point (value 0) with the smallest
2205 Euclidean distance to input points x[i], and n is the
2206 number of dimensions.
2208 Examples
2209 --------
2210 >>> from scipy import ndimage
2211 >>> import numpy as np
2212 >>> a = np.array(([0,1,1,1,1],
2213 ... [0,0,1,1,1],
2214 ... [0,1,1,1,1],
2215 ... [0,1,1,1,0],
2216 ... [0,1,1,0,0]))
2217 >>> ndimage.distance_transform_edt(a)
2218 array([[ 0. , 1. , 1.4142, 2.2361, 3. ],
2219 [ 0. , 0. , 1. , 2. , 2. ],
2220 [ 0. , 1. , 1.4142, 1.4142, 1. ],
2221 [ 0. , 1. , 1.4142, 1. , 0. ],
2222 [ 0. , 1. , 1. , 0. , 0. ]])
2224 With a sampling of 2 units along x, 1 along y:
2226 >>> ndimage.distance_transform_edt(a, sampling=[2,1])
2227 array([[ 0. , 1. , 2. , 2.8284, 3.6056],
2228 [ 0. , 0. , 1. , 2. , 3. ],
2229 [ 0. , 1. , 2. , 2.2361, 2. ],
2230 [ 0. , 1. , 2. , 1. , 0. ],
2231 [ 0. , 1. , 1. , 0. , 0. ]])
2233 Asking for indices as well:
2235 >>> edt, inds = ndimage.distance_transform_edt(a, return_indices=True)
2236 >>> inds
2237 array([[[0, 0, 1, 1, 3],
2238 [1, 1, 1, 1, 3],
2239 [2, 2, 1, 3, 3],
2240 [3, 3, 4, 4, 3],
2241 [4, 4, 4, 4, 4]],
2242 [[0, 0, 1, 1, 4],
2243 [0, 1, 1, 1, 4],
2244 [0, 0, 1, 4, 4],
2245 [0, 0, 3, 3, 4],
2246 [0, 0, 3, 3, 4]]])
2248 With arrays provided for inplace outputs:
2250 >>> indices = np.zeros(((np.ndim(a),) + a.shape), dtype=np.int32)
2251 >>> ndimage.distance_transform_edt(a, return_indices=True, indices=indices)
2252 array([[ 0. , 1. , 1.4142, 2.2361, 3. ],
2253 [ 0. , 0. , 1. , 2. , 2. ],
2254 [ 0. , 1. , 1.4142, 1.4142, 1. ],
2255 [ 0. , 1. , 1.4142, 1. , 0. ],
2256 [ 0. , 1. , 1. , 0. , 0. ]])
2257 >>> indices
2258 array([[[0, 0, 1, 1, 3],
2259 [1, 1, 1, 1, 3],
2260 [2, 2, 1, 3, 3],
2261 [3, 3, 4, 4, 3],
2262 [4, 4, 4, 4, 4]],
2263 [[0, 0, 1, 1, 4],
2264 [0, 1, 1, 1, 4],
2265 [0, 0, 1, 4, 4],
2266 [0, 0, 3, 3, 4],
2267 [0, 0, 3, 3, 4]]])
2269 """
2270 ft_inplace = isinstance(indices, numpy.ndarray)
2271 dt_inplace = isinstance(distances, numpy.ndarray)
2272 _distance_tranform_arg_check(
2273 dt_inplace, ft_inplace, return_distances, return_indices
2274 )
2276 # calculate the feature transform
2277 input = numpy.atleast_1d(numpy.where(input, 1, 0).astype(numpy.int8))
2278 if sampling is not None:
2279 sampling = _ni_support._normalize_sequence(sampling, input.ndim)
2280 sampling = numpy.asarray(sampling, dtype=numpy.float64)
2281 if not sampling.flags.contiguous:
2282 sampling = sampling.copy()
2284 if ft_inplace:
2285 ft = indices
2286 if ft.shape != (input.ndim,) + input.shape:
2287 raise RuntimeError('indices array has wrong shape')
2288 if ft.dtype.type != numpy.int32:
2289 raise RuntimeError('indices array must be int32')
2290 else:
2291 ft = numpy.zeros((input.ndim,) + input.shape, dtype=numpy.int32)
2293 _nd_image.euclidean_feature_transform(input, sampling, ft)
2294 # if requested, calculate the distance transform
2295 if return_distances:
2296 dt = ft - numpy.indices(input.shape, dtype=ft.dtype)
2297 dt = dt.astype(numpy.float64)
2298 if sampling is not None:
2299 for ii in range(len(sampling)):
2300 dt[ii, ...] *= sampling[ii]
2301 numpy.multiply(dt, dt, dt)
2302 if dt_inplace:
2303 dt = numpy.add.reduce(dt, axis=0)
2304 if distances.shape != dt.shape:
2305 raise RuntimeError('distances array has wrong shape')
2306 if distances.dtype.type != numpy.float64:
2307 raise RuntimeError('distances array must be float64')
2308 numpy.sqrt(dt, distances)
2309 else:
2310 dt = numpy.add.reduce(dt, axis=0)
2311 dt = numpy.sqrt(dt)
2313 # construct and return the result
2314 result = []
2315 if return_distances and not dt_inplace:
2316 result.append(dt)
2317 if return_indices and not ft_inplace:
2318 result.append(ft)
2320 if len(result) == 2:
2321 return tuple(result)
2322 elif len(result) == 1:
2323 return result[0]
2324 else:
2325 return None
2328def _distance_tranform_arg_check(distances_out, indices_out,
2329 return_distances, return_indices):
2330 """Raise a RuntimeError if the arguments are invalid"""
2331 error_msgs = []
2332 if (not return_distances) and (not return_indices):
2333 error_msgs.append(
2334 'at least one of return_distances/return_indices must be True')
2335 if distances_out and not return_distances:
2336 error_msgs.append(
2337 'return_distances must be True if distances is supplied'
2338 )
2339 if indices_out and not return_indices:
2340 error_msgs.append('return_indices must be True if indices is supplied')
2341 if error_msgs:
2342 raise RuntimeError(', '.join(error_msgs))