Files
scikit-image/skimage/morphology/misc.py
T
Olivia 9ea085fd6f Added new functionality to remove small holes from images.
This is currently working with **kwargs except for inplace
which I cannot get working. It works with arrays of type int
but returns an array of type bool. Possibly in future add
labelling for arrays of type int. A UserWarning is produced
when using arrays of type int which seems to work normally
but the test created for this does not pick up the warning.

Any assistance on these issues would be helpful. I started
this at EuroScipy 2015 and this fixes issue #1642
2015-08-30 23:05:15 +01:00

207 lines
7.3 KiB
Python

import numpy as np
import functools
import warnings
from scipy import ndimage as ndi
from .selem import _default_selem
# Our function names don't exactly correspond to ndimages.
# This dictionary translates from our names to scipy's.
funcs = ('erosion', 'dilation', 'opening', 'closing')
skimage2ndimage = dict((x, 'grey_' + x) for x in funcs)
# These function names are the same in ndimage.
funcs = ('binary_erosion', 'binary_dilation', 'binary_opening',
'binary_closing', 'black_tophat', 'white_tophat')
skimage2ndimage.update(dict((x, x) for x in funcs))
def default_selem(func):
"""Decorator to add a default structuring element to morphology functions.
Parameters
----------
func : function
A morphology function such as erosion, dilation, opening, closing,
white_tophat, or black_tophat.
Returns
-------
func_out : function
The function, using a default structuring element of same dimension
as the input image with connectivity 1.
"""
@functools.wraps(func)
def func_out(image, selem=None, *args, **kwargs):
if selem is None:
selem = _default_selem(image.ndim)
return func(image, selem=selem, *args, **kwargs)
return func_out
def remove_small_objects(ar, min_size=64, connectivity=1, in_place=False):
"""Remove connected components smaller than the specified size.
Parameters
----------
ar : ndarray (arbitrary shape, int or bool type)
The array containing the connected components of interest. If the array
type is int, it is assumed that it contains already-labeled objects.
The ints must be non-negative.
min_size : int, optional (default: 64)
The smallest allowable connected component size.
connectivity : int, {1, 2, ..., ar.ndim}, optional (default: 1)
The connectivity defining the neighborhood of a pixel.
in_place : bool, optional (default: False)
If `True`, remove the connected components in the input array itself.
Otherwise, make a copy.
Raises
------
TypeError
If the input array is of an invalid type, such as float or string.
ValueError
If the input array contains negative values.
Returns
-------
out : ndarray, same shape and type as input `ar`
The input array with small connected components removed.
Examples
--------
>>> from skimage import morphology
>>> a = np.array([[0, 0, 0, 1, 0],
... [1, 1, 1, 0, 0],
... [1, 1, 1, 0, 1]], bool)
>>> b = morphology.remove_small_objects(a, 6)
>>> b
array([[False, False, False, False, False],
[ True, True, True, False, False],
[ True, True, True, False, False]], dtype=bool)
>>> c = morphology.remove_small_objects(a, 7, connectivity=2)
>>> c
array([[False, False, False, True, False],
[ True, True, True, False, False],
[ True, True, True, False, False]], dtype=bool)
>>> d = morphology.remove_small_objects(a, 6, in_place=True)
>>> d is a
True
"""
# Should use `issubdtype` for bool below, but there's a bug in numpy 1.7
if not (ar.dtype == bool or np.issubdtype(ar.dtype, np.integer)):
raise TypeError("Only bool or integer image types are supported. "
"Got %s." % ar.dtype)
if in_place:
out = ar
else:
out = ar.copy()
if min_size == 0: # shortcut for efficiency
return out
if out.dtype == bool:
selem = ndi.generate_binary_structure(ar.ndim, connectivity)
ccs = np.zeros_like(ar, dtype=np.int32)
ndi.label(ar, selem, output=ccs)
else:
ccs = out
try:
component_sizes = np.bincount(ccs.ravel())
except ValueError:
raise ValueError("Negative value labels are not supported. Try "
"relabeling the input with `scipy.ndimage.label` or "
"`skimage.morphology.label`.")
if len(component_sizes) == 2:
warnings.warn("Only one label was provided to `remove_small_objects`. "
"Did you mean to use a boolean array?")
too_small = component_sizes < min_size
too_small_mask = too_small[ccs]
out[too_small_mask] = 0
return out
def remove_small_holes(ar, min_size=64, connectivity=1, in_place=False):
"""Remove connected components smaller than the specified size within a
larger connected object.
Parameters
----------
ar : ndarray (arbitrary shape, int or bool type)
The array containing the connected components of interest. If the array
type is int, it is assumed that it contains already-labeled objects. The
labels are not kept in the output image (this function always outputs
a bool image. It is suggested that labeling is completed after using
this function.
min_size : int, optional (default: 64)
The smallest allowable connected component size.
connectivity : int, {1, 2, ..., ar.ndim}, optional (default: 1)
The connectivity defining the neighborhood of a pixel.
in_place : bool, optional (default: False)
If `True`, remove the connected components in the input array itself.
Otherwise, make a copy.
Raises
------
TypeError
If the input array is of an invalid type, such as float or string.
ValueError
If the input array contains negative values.
Returns
-------
out : ndarray, same shape and type as input `ar`
The input array with small holes within connected components removed.
Examples
--------
>>> from skimage import morphology
>>> a = np.array([[1, 1, 1, 1, 1, 0],
... [1, 1, 1, 0, 1, 0],
... [1, 0, 0, 1, 1, 0],
... [1, 1, 1, 1, 1, 0]], bool)
>>> b = morphology.remove_small_holes(a, 2)
>>> b
array([[True, True, True, True, True, False],
[True, True, True, True, True, False],
[True, False, False, True, True, False],
[True, True, True, True, True, False]], dtype=bool)
>>> c = morphology.remove_small_holes(a, 2, connectivity=2)
>>> c
array([[True, True, True, True, True, False],
[True, True, True, False, True, False],
[True, False, False, True, True, False],
[True, True, True, True, True, False]], dtype=bool)
>>> d = morphology.remove_small_holes(a, 2, in_place=True)
>>> d is a
True
"""
# Should use `issubdtype` for bool below, but there's a bug in numpy 1.7
if not (ar.dtype == bool or np.issubdtype(ar.dtype, np.integer)):
raise TypeError("Only bool or integer image types are supported. "
"Got %s." % ar.dtype)
if in_place:
out = ar
else:
out = ar.copy()
#Creates warning if image is an integer image
if out.dtype != bool:
print("I'm about to warn")
warnings.warn("Any labeled images will be returned as a boolean array. "
"Did you mean to use a boolean array?", UserWarning)
#Creating the inverse of ar
out = np.logical_not(out)
#removing small objects from the inverse of ar
out = remove_small_objects(out, min_size, connectivity, in_place)
out = np.logical_not(out)
return out