mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-06 05:16:40 +08:00
Merge pull request #1689 from oew1v07/remove_small_holes
Remove small holes
This commit is contained in:
@@ -8,7 +8,7 @@ from .watershed import watershed
|
||||
from ._skeletonize import skeletonize, medial_axis
|
||||
from .convex_hull import convex_hull_image, convex_hull_object
|
||||
from .greyreconstruct import reconstruction
|
||||
from .misc import remove_small_objects
|
||||
from .misc import remove_small_objects, remove_small_holes
|
||||
|
||||
from ..measure._label import label
|
||||
from .._shared.utils import deprecated as _deprecated
|
||||
@@ -40,4 +40,5 @@ __all__ = ['binary_erosion',
|
||||
'convex_hull_image',
|
||||
'convex_hull_object',
|
||||
'reconstruction',
|
||||
'remove_small_objects']
|
||||
'remove_small_objects',
|
||||
'remove_small_holes']
|
||||
|
||||
@@ -37,7 +37,12 @@ def default_selem(func):
|
||||
return func(image, selem=selem, *args, **kwargs)
|
||||
|
||||
return func_out
|
||||
|
||||
|
||||
def _check_dtype_supported(ar):
|
||||
# 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)
|
||||
|
||||
def remove_small_objects(ar, min_size=64, connectivity=1, in_place=False):
|
||||
"""Remove connected components smaller than the specified size.
|
||||
@@ -88,10 +93,8 @@ def remove_small_objects(ar, min_size=64, connectivity=1, in_place=False):
|
||||
>>> 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)
|
||||
# Raising type error if not int or bool
|
||||
_check_dtype_supported(ar)
|
||||
|
||||
if in_place:
|
||||
out = ar
|
||||
@@ -124,3 +127,89 @@ def remove_small_objects(ar, min_size=64, connectivity=1, in_place=False):
|
||||
out[too_small_mask] = 0
|
||||
|
||||
return out
|
||||
|
||||
def remove_small_holes(ar, min_size=64, connectivity=1, in_place=False):
|
||||
"""Remove continguous holes smaller than the specified size.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ar : ndarray (arbitrary shape, int or bool type)
|
||||
The array containing the connected components of interest.
|
||||
min_size : int, optional (default: 64)
|
||||
The hole 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
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
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.
|
||||
"""
|
||||
_check_dtype_supported(ar)
|
||||
|
||||
#Creates warning if image is an integer image
|
||||
if ar.dtype != bool:
|
||||
warnings.warn("Any labeled images will be returned as a boolean array. "
|
||||
"Did you mean to use a boolean array?", UserWarning)
|
||||
|
||||
if in_place:
|
||||
out = ar
|
||||
else:
|
||||
out = ar.copy()
|
||||
|
||||
#Creating the inverse of ar
|
||||
if in_place:
|
||||
out = np.logical_not(out,out)
|
||||
else:
|
||||
out = np.logical_not(out)
|
||||
|
||||
#removing small objects from the inverse of ar
|
||||
out = remove_small_objects(out, min_size, connectivity, in_place)
|
||||
|
||||
if in_place:
|
||||
out = np.logical_not(out,out)
|
||||
else:
|
||||
out = np.logical_not(out)
|
||||
|
||||
return out
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import numpy as np
|
||||
from numpy.testing import (assert_array_equal, assert_equal, assert_raises,
|
||||
assert_warns)
|
||||
from skimage.morphology import remove_small_objects
|
||||
from skimage.morphology import remove_small_objects, remove_small_holes
|
||||
from ..._shared._warnings import expected_warnings
|
||||
|
||||
test_image = np.array([[0, 0, 0, 1, 0],
|
||||
[1, 1, 1, 0, 0],
|
||||
@@ -60,7 +61,8 @@ def test_single_label_warning():
|
||||
image = np.array([[0, 0, 0, 1, 0],
|
||||
[1, 1, 1, 0, 0],
|
||||
[1, 1, 1, 0, 0]], int)
|
||||
assert_warns(UserWarning, remove_small_objects, image, min_size=6)
|
||||
with expected_warnings(['use a boolean array?']):
|
||||
remove_small_objects(image, min_size=6)
|
||||
|
||||
|
||||
def test_float_input():
|
||||
@@ -72,6 +74,102 @@ def test_negative_input():
|
||||
negative_int = np.random.randint(-4, -1, size=(5, 5))
|
||||
assert_raises(ValueError, remove_small_objects, negative_int)
|
||||
|
||||
test_holes_image = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,0,0,1,1,0,0,0,0],
|
||||
[0,1,1,1,0,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,0,1],
|
||||
[0,0,0,0,0,0,0,1,1,1]], bool)
|
||||
|
||||
def test_one_connectivity_holes():
|
||||
expected = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1]], bool)
|
||||
observed = remove_small_holes(test_holes_image, min_size=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_two_connectivity_holes():
|
||||
expected = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,0,0,1,1,0,0,0,0],
|
||||
[0,1,1,1,0,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1]], bool)
|
||||
observed = remove_small_holes(test_holes_image, min_size=3, connectivity=2)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
def test_in_place_holes():
|
||||
observed = remove_small_holes(test_holes_image, min_size=3, in_place=True)
|
||||
assert_equal(observed is test_holes_image, True,
|
||||
"remove_small_holes in_place argument failed.")
|
||||
|
||||
def test_labeled_image_holes():
|
||||
labeled_holes_image = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,0,0,1,1,0,0,0,0],
|
||||
[0,1,1,1,0,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,2,2,2],
|
||||
[0,0,0,0,0,0,0,2,0,2],
|
||||
[0,0,0,0,0,0,0,2,2,2]], dtype=int)
|
||||
expected = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1]], dtype=bool)
|
||||
observed = remove_small_holes(labeled_holes_image, min_size=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_uint_image_holes():
|
||||
labeled_holes_image = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,0,0,1,1,0,0,0,0],
|
||||
[0,1,1,1,0,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,2,2,2],
|
||||
[0,0,0,0,0,0,0,2,0,2],
|
||||
[0,0,0,0,0,0,0,2,2,2]], dtype=np.uint8)
|
||||
expected = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,0,0,1,1,1]], dtype=bool)
|
||||
observed = remove_small_holes(labeled_holes_image, min_size=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_label_warning_holes():
|
||||
labeled_holes_image = np.array([[0,0,0,0,0,0,1,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,1,0,0,1,1,0,0,0,0],
|
||||
[0,1,1,1,0,1,0,0,0,0],
|
||||
[0,1,1,1,1,1,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,2,2,2],
|
||||
[0,0,0,0,0,0,0,2,0,2],
|
||||
[0,0,0,0,0,0,0,2,2,2]], dtype=int)
|
||||
with expected_warnings(['use a boolean array?']):
|
||||
remove_small_holes(labeled_holes_image, min_size=3)
|
||||
|
||||
def test_float_input_holes():
|
||||
float_test = np.random.rand(5, 5)
|
||||
assert_raises(TypeError, remove_small_holes, float_test)
|
||||
|
||||
if __name__ == "__main__":
|
||||
np.testing.run_module_suite()
|
||||
|
||||
Reference in New Issue
Block a user