# Copyright (C) 2003-2005 Peter J. Verveer
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# 3. The name of the author may not be used to endorse or promote
# products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import division, print_function, absolute_import
import warnings
import operator
import numpy
from . import _ni_support
from . import _nd_image
from . import filters
__all__ = ['iterate_structure', 'generate_binary_structure', 'binary_erosion',
'binary_dilation', 'binary_opening', 'binary_closing',
'binary_hit_or_miss', 'binary_propagation', 'binary_fill_holes',
'grey_erosion', 'grey_dilation', 'grey_opening', 'grey_closing',
'morphological_gradient', 'morphological_laplace', 'white_tophat',
'black_tophat', 'distance_transform_bf', 'distance_transform_cdt',
'distance_transform_edt']
def _center_is_true(structure, origin):
structure = numpy.array(structure)
coor = tuple([oo + ss // 2 for ss, oo in zip(structure.shape,
origin)])
return bool(structure[coor])
def iterate_structure(structure, iterations, origin=None):
"""
Iterate a structure by dilating it with itself.
Parameters
----------
structure : array_like
Structuring element (an array of bools, for example), to be dilated with
itself.
iterations : int
number of dilations performed on the structure with itself
origin : optional
If origin is None, only the iterated structure is returned. If
not, a tuple of the iterated structure and the modified origin is
returned.
Returns
-------
iterate_structure : ndarray of bools
A new structuring element obtained by dilating `structure`
(`iterations` - 1) times with itself.
See also
--------
generate_binary_structure
Examples
--------
>>> from scipy import ndimage
>>> struct = ndimage.generate_binary_structure(2, 1)
>>> struct.astype(int)
array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]])
>>> ndimage.iterate_structure(struct, 2).astype(int)
array([[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[1, 1, 1, 1, 1],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0]])
>>> ndimage.iterate_structure(struct, 3).astype(int)
array([[0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0]])
"""
structure = numpy.asarray(structure)
if iterations < 2:
return structure.copy()
ni = iterations - 1
shape = [ii + ni * (ii - 1) for ii in structure.shape]
pos = [ni * (structure.shape[ii] // 2) for ii in range(len(shape))]
slc = tuple(slice(pos[ii], pos[ii] + structure.shape[ii], None)
for ii in range(len(shape)))
out = numpy.zeros(shape, bool)
out[slc] = structure != 0
out = binary_dilation(out, structure, iterations=ni)
if origin is None:
return out
else:
origin = _ni_support._normalize_sequence(origin, structure.ndim)
origin = [iterations * o for o in origin]
return out, origin
def generate_binary_structure(rank, connectivity):
"""
Generate a binary structure for binary morphological operations.
Parameters
----------
rank : int
Number of dimensions of the array to which the structuring element
will be applied, as returned by `np.ndim`.
connectivity : int
`connectivity` determines which elements of the output array belong
to the structure, i.e. are considered as neighbors of the central
element. Elements up to a squared distance of `connectivity` from
the center are considered neighbors. `connectivity` may range from 1
(no diagonal elements are neighbors) to `rank` (all elements are
neighbors).
Returns
-------
output : ndarray of bools
Structuring element which may be used for binary morphological
operations, with `rank` dimensions and all dimensions equal to 3.
See also
--------
iterate_structure, binary_dilation, binary_erosion
Notes
-----
`generate_binary_structure` can only create structuring elements with
dimensions equal to 3, i.e. minimal dimensions. For larger structuring
elements, that are useful e.g. for eroding large objects, one may either
use `iterate_structure`, or create directly custom arrays with
numpy functions such as `numpy.ones`.
Examples
--------
>>> from scipy import ndimage
>>> struct = ndimage.generate_binary_structure(2, 1)
>>> struct
array([[False, True, False],
[ True, True, True],
[False, True, False]], dtype=bool)
>>> a = np.zeros((5,5))
>>> a[2, 2] = 1
>>> a
array([[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]])
>>> b = ndimage.binary_dilation(a, structure=struct).astype(a.dtype)
>>> b
array([[ 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 1., 1., 1., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 0.]])
>>> ndimage.binary_dilation(b, structure=struct).astype(a.dtype)
array([[ 0., 0., 1., 0., 0.],
[ 0., 1., 1., 1., 0.],
[ 1., 1., 1., 1., 1.],
[ 0., 1., 1., 1., 0.],
[ 0., 0., 1., 0., 0.]])
>>> struct = ndimage.generate_binary_structure(2, 2)
>>> struct
array([[ True, True, True],
[ True, True, True],
[ True, True, True]], dtype=bool)
>>> struct = ndimage.generate_binary_structure(3, 1)
>>> struct # no diagonal elements
array([[[False, False, False],
[False, True, False],
[False, False, False]],
[[False, True, False],
[ True, True, True],
[False, True, False]],
[[False, False, False],
[False, True, False],
[False, False, False]]], dtype=bool)
"""
if connectivity < 1:
connectivity = 1
if rank < 1:
return numpy.array(True, dtype=bool)
output = numpy.fabs(numpy.indices([3] * rank) - 1)
output = numpy.add.reduce(output, 0)
return output <= connectivity
def _binary_erosion(input, structure, iterations, mask, output,
border_value, origin, invert, brute_force):
try:
iterations = operator.index(iterations)
except TypeError:
raise TypeError('iterations parameter should be an integer')
input = numpy.asarray(input)
if numpy.iscomplexobj(input):
raise TypeError('Complex type not supported')
if structure is None:
structure = generate_binary_structure(input.ndim, 1)
else:
structure = numpy.asarray(structure, dtype=bool)
if structure.ndim != input.ndim:
raise RuntimeError('structure and input must have same dimensionality')
if not structure.flags.contiguous:
structure = structure.copy()
if numpy.prod(structure.shape, axis=0) < 1:
raise RuntimeError('structure must not be empty')
if mask is not None:
mask = numpy.asarray(mask)
if mask.shape != input.shape:
raise RuntimeError('mask and input must have equal sizes')
origin = _ni_support._normalize_sequence(origin, input.ndim)
cit = _center_is_true(structure, origin)
if isinstance(output, numpy.ndarray):
if numpy.iscomplexobj(output):
raise TypeError('Complex output type not supported')
else:
output = bool
output = _ni_support._get_output(output, input)
if iterations == 1:
_nd_image.binary_erosion(input, structure, mask, output,
border_value, origin, invert, cit, 0)
return output
elif cit and not brute_force:
changed, coordinate_list = _nd_image.binary_erosion(
input, structure, mask, output,
border_value, origin, invert, cit, 1)
structure = structure[tuple([slice(None, None, -1)] *
structure.ndim)]
for ii in range(len(origin)):
origin[ii] = -origin[ii]
if not structure.shape[ii] & 1:
origin[ii] -= 1
if mask is not None:
mask = numpy.asarray(mask, dtype=numpy.int8)
if not structure.flags.contiguous:
structure = structure.copy()
_nd_image.binary_erosion2(output, structure, mask, iterations - 1,
origin, invert, coordinate_list)
return output
else:
tmp_in = numpy.empty_like(input, dtype=bool)
tmp_out = output
if iterations >= 1 and not iterations & 1:
tmp_in, tmp_out = tmp_out, tmp_in
changed = _nd_image.binary_erosion(
input, structure, mask, tmp_out,
border_value, origin, invert, cit, 0)
ii = 1
while ii < iterations or (iterations < 1 and changed):
tmp_in, tmp_out = tmp_out, tmp_in
changed = _nd_image.binary_erosion(
tmp_in, structure, mask, tmp_out,
border_value, origin, invert, cit, 0)
ii += 1
return output
def binary_erosion(input, structure=None, iterations=1, mask=None, output=None,
border_value=0, origin=0, brute_force=False):
"""
Multi-dimensional binary erosion with a given structuring element.
Binary erosion is a mathematical morphology operation used for image
processing.
Parameters
----------
input : array_like
Binary image to be eroded. Non-zero (True) elements form
the subset to be eroded.
structure : array_like, optional
Structuring element used for the erosion. Non-zero elements are
considered True. If no structuring element is provided, an element
is generated with a square connectivity equal to one.
iterations : int, optional
The erosion is repeated `iterations` times (one, by default).
If iterations is less than 1, the erosion is repeated until the
result does not change anymore.
mask : array_like, optional
If a mask is given, only those elements with a True value at
the corresponding mask element are modified at each iteration.
output : ndarray, optional
Array of the same shape as input, into which the output is placed.
By default, a new array is created.
border_value : int (cast to 0 or 1), optional
Value at the border in the output array.
origin : int or tuple of ints, optional
Placement of the filter, by default 0.
brute_force : boolean, optional
Memory condition: if False, only the pixels whose value was changed in
the last iteration are tracked as candidates to be updated (eroded) in
the current iteration; if True all pixels are considered as candidates
for erosion, regardless of what happened in the previous iteration.
False by default.
Returns
-------
binary_erosion : ndarray of bools
Erosion of the input by the structuring element.
See also
--------
grey_erosion, binary_dilation, binary_closing, binary_opening,
generate_binary_structure
Notes
-----
Erosion [1]_ is a mathematical morphology operation [2]_ that uses a
structuring element for shrinking the shapes in an image. The binary
erosion of an image by a structuring element is the locus of the points
where a superimposition of the structuring element centered on the point
is entirely contained in the set of non-zero elements of the image.
References
----------
Loading ...