mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-29 05:01:42 +08:00
120 lines
3.6 KiB
Python
120 lines
3.6 KiB
Python
__all__ = ['convex_hull_image', 'convex_hull_object']
|
|
|
|
import numpy as np
|
|
from ._pnpoly import grid_points_inside_poly
|
|
from ._convex_hull import possible_hull
|
|
from ..measure._label import label
|
|
from skimage.util import unique_rows
|
|
|
|
|
|
def convex_hull_image(image):
|
|
"""Compute the convex hull image of a binary image.
|
|
|
|
The convex hull is the set of pixels included in the smallest convex
|
|
polygon that surround all white pixels in the input image.
|
|
|
|
Parameters
|
|
----------
|
|
image : ndarray
|
|
Binary input image. This array is cast to bool before processing.
|
|
|
|
Returns
|
|
-------
|
|
hull : ndarray of bool
|
|
Binary image with pixels in convex hull set to True.
|
|
|
|
References
|
|
----------
|
|
.. [1] http://blogs.mathworks.com/steve/2011/10/04/binary-image-convex-hull-algorithm-notes/
|
|
|
|
"""
|
|
|
|
image = image.astype(bool)
|
|
|
|
# Here we do an optimisation by choosing only pixels that are
|
|
# the starting or ending pixel of a row or column. This vastly
|
|
# limits the number of coordinates to examine for the virtual
|
|
# hull.
|
|
coords = possible_hull(image.astype(np.uint8))
|
|
N = len(coords)
|
|
|
|
# Add a vertex for the middle of each pixel edge
|
|
coords_corners = np.empty((N * 4, 2))
|
|
for i, (x_offset, y_offset) in enumerate(zip((0, 0, -0.5, 0.5),
|
|
(-0.5, 0.5, 0, 0))):
|
|
coords_corners[i * N:(i + 1) * N] = coords + [x_offset, y_offset]
|
|
|
|
# repeated coordinates can *sometimes* cause problems in
|
|
# scipy.spatial.Delaunay, so we remove them.
|
|
coords = unique_rows(coords_corners)
|
|
|
|
try:
|
|
from scipy.spatial import Delaunay
|
|
except ImportError:
|
|
raise ImportError('Could not import scipy.spatial, only available in '
|
|
'scipy >= 0.9.')
|
|
|
|
# Subtract offset
|
|
offset = coords.mean(axis=0)
|
|
coords -= offset
|
|
|
|
# Find the convex hull
|
|
chull = Delaunay(coords).convex_hull
|
|
v = coords[np.unique(chull)]
|
|
|
|
# Sort vertices clock-wise
|
|
v_centred = v - v.mean(axis=0)
|
|
angles = np.arctan2(v_centred[:, 0], v_centred[:, 1])
|
|
v = v[np.argsort(angles)]
|
|
|
|
# Add back offset
|
|
v += offset
|
|
|
|
# For each pixel coordinate, check whether that pixel
|
|
# lies inside the convex hull
|
|
mask = grid_points_inside_poly(image.shape[:2], v)
|
|
|
|
return mask
|
|
|
|
|
|
def convex_hull_object(image, neighbors=8):
|
|
"""Compute the convex hull image of individual objects in a binary image.
|
|
|
|
The convex hull is the set of pixels included in the smallest convex
|
|
polygon that surround all white pixels in the input image.
|
|
|
|
Parameters
|
|
----------
|
|
image : ndarray
|
|
Binary input image.
|
|
neighbors : {4, 8}, int
|
|
Whether to use 4- or 8-connectivity.
|
|
|
|
Returns
|
|
-------
|
|
hull : ndarray of bool
|
|
Binary image with pixels in convex hull set to True.
|
|
|
|
Notes
|
|
-----
|
|
This function uses skimage.morphology.label to define unique objects,
|
|
finds the convex hull of each using convex_hull_image, and combines
|
|
these regions with logical OR. Be aware the convex hulls of unconnected
|
|
objects may overlap in the result. If this is suspected, consider using
|
|
convex_hull_image separately on each object.
|
|
|
|
"""
|
|
|
|
if neighbors != 4 and neighbors != 8:
|
|
raise ValueError('Neighbors must be either 4 or 8.')
|
|
|
|
labeled_im = label(image, neighbors, background=0)
|
|
convex_obj = np.zeros(image.shape, dtype=bool)
|
|
convex_img = np.zeros(image.shape, dtype=bool)
|
|
|
|
for i in range(0, labeled_im.max() + 1):
|
|
convex_obj = convex_hull_image(labeled_im == i)
|
|
convex_img = np.logical_or(convex_img, convex_obj)
|
|
|
|
return convex_img
|