From 226eaaff9250da4e73e4f0589cea41a13d961130 Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Tue, 20 Nov 2012 00:12:47 -0600 Subject: [PATCH] FEAT - Unified peak finder backend Detailed changelist: * Fully documented new API for peak_local_max() * Removed commented-out old code for is_local_maximum() * Added "See also" and "Notes" to is_local_maximum() redirecting to peak_local_max() --- skimage/feature/peak.py | 58 +++++++++++++++++++++------- skimage/morphology/watershed.py | 67 ++++++--------------------------- 2 files changed, 56 insertions(+), 69 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index b1148c9a..cd740656 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -4,10 +4,10 @@ import scipy.ndimage as ndi def peak_local_max(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None, **kwargs): - """Return coordinates of peaks in an image. + """ + Find peaks in an image, and return them as coordinates or a boolean array. - Peaks are the local maxima in a region of `2 * min_distance + 1` - (i.e. peaks are separated by at least `min_distance`). + Peaks are the local maxima NOTE: If peaks are flat (i.e. multiple pixels have exact same intensity), the coordinates of all pixels are returned. @@ -16,22 +16,54 @@ def peak_local_max(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, ---------- image : ndarray of floats Input image. - min_distance : int - Minimum number of pixels separating peaks and image boundary. - threshold : float - Deprecated. See `threshold_rel`. - threshold_abs : float + + min_distance : int, default 10. + Minimum number of pixels separating peaks in a region of `2 * + min_distance + 1` (i.e. peaks are separated by at least + `min_distance`). + If `exclude_border` is True, this value also excludes a border + `min_distance` from the image boundary. + To find the maximum number of points, use `min_distance=1`. + + threshold_abs : float, default 0. Minimum intensity of peaks. - threshold_rel : float + + threshold_rel : float, default 0.1 Minimum intensity of peaks calculated as `max(image) * threshold_rel`. - num_peaks : int + + exclude_border : bool, default True + If True, `min_distance` excludes peaks from the border of the image as + well as from each other. + + indices : bool, default True + If True, the output will be a matrix representing peak coordinates. + If False, the output will be a boolean matrix shaped as `image.shape` + with peaks present at True elements. + + num_peaks : int, default np.inf Maximum number of peaks. When the number of peaks exceeds `num_peaks`, - return `num_peaks` coordinates based on peak intensity. + return `num_peaks` peaks based on highest peak intensity. + + footprint : ndarray of bools, optional + If provided, `footprint == 1` represents the local region within which + to search for peaks at every point in `image`. + Overrides `min_distance`, except for border exclusion if + `exclude_border` is True. + + labels : ndarray of ints, optional + If provided, each unique region `labels == value` represents a unique + region to search for peaks. Zero is reserved for background. + + threshold : float, optional + Deprecated. If provided as a kwarg, will override `threshold_rel`. + See `threshold_rel`. Returns ------- - coordinates : (N, 2) array - (row, column) coordinates of peaks. + output : (N, 2) array or ndarray of bools + If `exclude_border = True` : (row, column) coordinates of peaks. + If `exclude_border = False` : Boolean array shaped like `image`, + with peaks represented by True values. Notes ----- diff --git a/skimage/morphology/watershed.py b/skimage/morphology/watershed.py index 840d4c90..f4c5a816 100644 --- a/skimage/morphology/watershed.py +++ b/skimage/morphology/watershed.py @@ -248,6 +248,16 @@ def is_local_maximum(image, labels=None, footprint=None): result: ndarray of bools mask that is True for pixels that are local maxima of `image` + See also + -------- + skimage.feature.peak_local_max: Unified peak finding backend. + The more capable backend for finding local maxima. + + Notes + ----- + This function is now a wrapper for skimage.feature.peak_local_max() and is + retained only for convenience and backward compatibility. + Examples -------- >>> image = np.zeros((4, 4)) @@ -281,63 +291,8 @@ def is_local_maximum(image, labels=None, footprint=None): [False, True, False, True], [False, False, False, False], [False, True, False, True]], dtype=bool) + """ - # if labels is None: - # labels = np.ones(image.shape, dtype=np.uint8) - # if footprint is None: - # footprint = np.ones([3] * image.ndim, dtype=np.uint8) - # assert((np.all(footprint.shape) & 1) == 1) - # footprint = (footprint != 0) - # footprint_extent = (np.array(footprint.shape) - 1) // 2 - # if np.all(footprint_extent == 0): - # return labels > 0 - # result = (labels > 0).copy() - # # - # # Create a labels matrix with zeros at the borders that might be - # # hit by the footprint. - # # - # big_labels = np.zeros(np.array(labels.shape) + footprint_extent * 2, - # labels.dtype) - # big_labels[[slice(fe, -fe) for fe in footprint_extent]] = labels - # # - # # Find the relative indexes of each footprint element - # # - # image_strides = np.array(image.strides) // image.dtype.itemsize - # big_strides = np.array(big_labels.strides) // big_labels.dtype.itemsize - # result_strides = np.array(result.strides) // result.dtype.itemsize - # footprint_offsets = np.mgrid[[slice(-fe, fe + 1) for fe in footprint_extent]] - - # fp_image_offsets = np.sum(image_strides[:, np.newaxis] * - # footprint_offsets[:, footprint], 0) - # fp_big_offsets = np.sum(big_strides[:, np.newaxis] * - # footprint_offsets[:, footprint], 0) - # # - # # Get the index of each labeled pixel in the image and big_labels arrays - # # - # indexes = np.mgrid[[slice(0, x) for x in labels.shape]][:, labels > 0] - # image_indexes = np.sum(image_strides[:, np.newaxis] * indexes, 0) - # big_indexes = np.sum(big_strides[:, np.newaxis] * - # (indexes + footprint_extent[:, np.newaxis]), 0) - # result_indexes = np.sum(result_strides[:, np.newaxis] * indexes, 0) - # # - # # Now operate on the raveled images - # # - # big_labels_raveled = big_labels.ravel() - # image_raveled = image.ravel() - # result_raveled = result.ravel() - # # - # # A hit is a hit if the label at the offset matches the label at the pixel - # # and if the intensity at the pixel is greater or equal to the intensity - # # at the offset. - # # - # for fp_image_offset, fp_big_offset in zip(fp_image_offsets, fp_big_offsets): - # same_label = (big_labels_raveled[big_indexes + fp_big_offset] == - # big_labels_raveled[big_indexes]) - # less_than = (image_raveled[image_indexes[same_label]] < - # image_raveled[image_indexes[same_label] + fp_image_offset]) - # result_raveled[result_indexes[same_label][less_than]] = False - - # return result return peak_local_max(image, labels=labels, min_distance=1, footprint=footprint, indices=False, exclude_border=False)