-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convex hull object implementation #522
Changes from all commits
3e2318f
549149d
32c571c
873caec
1b78ba5
281d967
893d8a4
6825261
da71b89
3176280
f2f17ec
3cab568
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,148 @@ | ||
__all__ = ['convex_hull_image'] | ||
__all__ = ['convex_hull_image', 'connected_component', 'convex_hull_object'] | ||
|
||
import numpy as np | ||
from ._pnpoly import grid_points_inside_poly | ||
from ._convex_hull import possible_hull | ||
|
||
from .selem import square as sq | ||
from skimage.morphology import label, dilation | ||
|
||
def convex_hull_image(image): | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whitespace noise shouldn't be in the PR. Configure your editor to change all tabs to spaces, to remove all trailing whitespace. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am outside right now, but I will sit and configure this once and for all. Sorry for this. Any suggestions on the preferable text editor to use on OS X ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sublime is hard to beat. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi Josh, Aah thanks for the suggestion. I'll try it out. Chintak |
||
"""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 uint8 | ||
Binary image with pixels in convex hull set to 255. | ||
|
||
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] | ||
|
||
coords = coords_corners | ||
|
||
try: | ||
from scipy.spatial import Delaunay | ||
except ImportError: | ||
raise ImportError('Could not import scipy.spatial, only available in ' | ||
'scipy >= 0.9.') | ||
|
||
coords = coords_corners | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change will certainly make things break |
||
try: | ||
from scipy.spatial import Delaunay | ||
except ImportError: | ||
raise ImportError('Could not import scipy.spatial, only available in ' | ||
'scipy >= 0.9.') | ||
|
||
# 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)] | ||
|
||
# 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 connected_component(image, start_pixel_tuple): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do we get from this function that we don't get from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but in this case we can probably use the labeled image to get the components we want as masks. As discussed on the mailing list today, it does look like a good and fast floodfill algorithm will be useful. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be even more useful if it was nD or at least 3D from the start... On Tue, Apr 23, 2013 at 12:15 AM, Stefan van der Walt <
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To start with, I knew of the implementation using repetitive dilation. But |
||
|
||
"""Compute the connected object to a given starting pixel with | ||
8-connectivity for 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. | ||
|
||
start_pixel_tuple : tuple of int | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Of the form (x, y) which represents the index of the starting pixel | ||
|
||
Returns | ||
------- | ||
obj : ndarray of uint8 | ||
Binary image with pixels in convex hull set to True. | ||
|
||
""" | ||
|
||
next_im = np.zeros(image.shape, dtype=np.uint8) | ||
next_im[start_pixel_tuple] = 1 | ||
start_im = np.zeros(image.shape) | ||
# Structuring element for Dilation: square of side 3 with all elements 1. | ||
while not np.array_equal(start_im, next_im): | ||
start_im = next_im.copy() | ||
dilated_im = dilation(start_im, sq(3)) | ||
next_im = dilated_im & image | ||
|
||
return next_im | ||
|
||
def convex_hull_object(image, output_form=None): | ||
|
||
"""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. | ||
|
||
output_form : string | ||
if 'single' then outputs a 3D array with separate convex hull computed | ||
for individual objects, where the 3rd index is used to change the object | ||
Default is None, in which case it outputs the convex hull for all | ||
objects individually as a single 2D array | ||
|
||
Returns | ||
------- | ||
hull : ndarray of bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Convex hulls can overlap, so this is not a suitable way to represent the output. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see matlab does it this way too. I'm not sure how I feel about that. However, since we don't currently have a better way of representing connected components (this is something we can also consider implementing), I guess the union of the hulls of the images (as you have it here) will do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would you prefer the output to be ? That is why there is also an option to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thanks, I only saw that output flag now. I think |
||
Binary image with pixels in convex hull set to True. | ||
|
||
""" | ||
|
||
# We add 1 to the output of label() so as to make the | ||
# background 0 rather than -1 | ||
|
||
(m, n) = image.shape | ||
convex_out = np.zeros((m, n), dtype=bool) | ||
labeled_im = label(image, neighbors=8, background=0) + | ||
segmented_objs = np.zeros((m, n, labeled_im.max()), dtype=bool) | ||
convex_objs = np.zeros((m, n, labeled_im.max()), dtype=bool) | ||
|
||
for i in range(1, labeled_im.max()+1): | ||
|
||
start_pixel_tuple = tuple(transpose(np.where(labeled_im == i))[0]) | ||
segmented_objs[:, :, i-1] = connected_component(image, start_pixel_tuple) | ||
convex_objs[:, :, i-1] = convex_hull_image(segmented_objs[:, :, i-1]) | ||
convex_out |= convex_objs[:, :, i-1] | ||
|
||
if output_form is 'single': | ||
return convex_objs | ||
|
||
if output_form is None: | ||
return convex_out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep it as
square
--the 4 character savings isnt worth it.