Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor and fix peak_local_max (#4760)
* Remove relabelling * Add trivial case support in label regions * Fix min_distance support * Fix test_peak * Fix test_corner * Fix corner_peaks doctest * Vectorize KDtree query * Update skimage/feature/peak.py Co-authored-by: Marianne Corvellec <marianne.corvellec@ens-lyon.org> * Fix merge conflicts * Fix docstring * Refactor _get_high_intensity_peaks * Add ensure_spacing function * Update _get_threshold * Update _exclude_border * Remove warning in test * Add docstring for _get_threshold * Add some comments to ease review * Fix test_input_labels_unmodified * Add note about behaviour change in 0.18 * Fix ensure_spacing * Revert test_peak.test_empty * Fix ensure_spacing * Revert corner_peaks doctest * Revert test_disk * Simplify test_input_labels_unmodified * Revert warning msg when footprint.size < 2 * Update skimage/_shared/tests/test_coord.py Co-authored-by: Gregory R. Lee <grlee77@gmail.com> * Add release notes * Remove warning if footprint.size=1: the warning assertion stands only if min_distance < 1 Co-authored-by: Marianne Corvellec <marianne.corvellec@ens-lyon.org> Co-authored-by: Riadh <r.fezzani@vitadx.com> Co-authored-by: Juan Nunez-Iglesias <juan.nunez-iglesias@monash.edu> Co-authored-by: Gregory R. Lee <grlee77@gmail.com>
- Loading branch information
1 parent
e7466ca
commit a04bd6b
Showing
6 changed files
with
295 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import numpy as np | ||
from scipy.spatial import cKDTree, distance | ||
|
||
|
||
def ensure_spacing(coord, spacing=1, p_norm=np.inf): | ||
"""Returns a subset of coord where a minimum spacing is guaranteed. | ||
Parameters | ||
---------- | ||
coord : ndarray | ||
The coordinates of the considered points. | ||
spacing : float | ||
the maximum allowed spacing between the points. | ||
p_norm : float | ||
Which Minkowski p-norm to use. Should be in the range [1, inf]. | ||
A finite large p may cause a ValueError if overflow can occur. | ||
``inf`` corresponds to the Chebyshev distance and 2 to the | ||
Euclidean distance. | ||
Returns | ||
------- | ||
output : ndarray | ||
A subset of coord where a minimum spacing is guaranteed. | ||
""" | ||
|
||
output = coord | ||
if len(coord): | ||
# Use KDtree to find the peaks that are too close to each other | ||
tree = cKDTree(coord) | ||
|
||
indices = tree.query_ball_point(coord, r=spacing, p=p_norm) | ||
rejected_peaks_indices = set() | ||
for idx, candidates in enumerate(indices): | ||
if idx not in rejected_peaks_indices: | ||
# keep current point and the points at exactly spacing from it | ||
candidates.remove(idx) | ||
dist = distance.cdist([coord[idx]], | ||
coord[candidates], | ||
distance.minkowski, | ||
p=p_norm).reshape(-1) | ||
candidates = [c for c, d in zip(candidates, dist) | ||
if d < spacing] | ||
|
||
# candidates.remove(keep) | ||
rejected_peaks_indices.update(candidates) | ||
|
||
# Remove the peaks that are too close to each other | ||
output = np.delete(coord, tuple(rejected_peaks_indices), axis=0) | ||
|
||
return output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import pytest | ||
import numpy as np | ||
from scipy.spatial.distance import pdist, minkowski | ||
from skimage._shared.coord import ensure_spacing | ||
|
||
|
||
@pytest.mark.parametrize("p", [1, 2, np.inf]) | ||
def test_ensure_spacing_trivial(p): | ||
# --- Empty input | ||
assert ensure_spacing([], p_norm=p) == [] | ||
|
||
# --- Verified spacing | ||
coord = np.random.randn(100, 2) | ||
|
||
# --- 0 spacing | ||
assert np.array_equal(coord, ensure_spacing(coord, spacing=0, p_norm=p)) | ||
|
||
# Spacing is chosen to be half the minimum distance | ||
spacing = pdist(coord, minkowski, p=p).min() * 0.5 | ||
|
||
out = ensure_spacing(coord, spacing=spacing, p_norm=p) | ||
|
||
assert np.array_equal(coord, out) | ||
|
||
|
||
@pytest.mark.parametrize("ndim", [1, 2, 3, 4, 5]) | ||
def test_ensure_spacing_nD(ndim): | ||
coord = np.ones((5, ndim)) | ||
|
||
expected = np.ones((1, ndim)) | ||
|
||
assert np.array_equal(ensure_spacing(coord), expected) | ||
|
||
|
||
@pytest.mark.parametrize("p", [1, 2, np.inf]) | ||
def test_ensure_spacing_p_norm(p): | ||
coord = np.random.randn(100, 2) | ||
|
||
# --- Consider the average distance btween the point as spacing | ||
spacing = np.median(pdist(coord, minkowski, p)) | ||
out = ensure_spacing(coord, spacing=spacing, p_norm=p) | ||
|
||
assert pdist(out, minkowski, p).min() > spacing |
Oops, something went wrong.