Skip to content
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

Refactor and fix peak_local_max #4760

Merged
merged 43 commits into from Nov 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
db38358
Move trivial check to _get_peak_mask
rfezzani May 22, 2020
2cafddd
Merge branch 'master' of https://github.com/scikit-image/scikit-image
rfezzani May 22, 2020
3f86cb5
Revert "Move trivial check to _get_peak_mask"
rfezzani May 22, 2020
e9c41fa
Merge branch 'master' of https://github.com/scikit-image/scikit-image
rfezzani May 26, 2020
746be9e
Remove relabelling
rfezzani May 27, 2020
6856683
Add trivial case support in label regions
rfezzani May 27, 2020
2d70f98
Fix min_distance support
rfezzani May 27, 2020
c5a7890
Fix test_peak
rfezzani May 27, 2020
1770f42
Fix test_corner
rfezzani May 27, 2020
99c29f1
Merge branch 'master' of https://github.com/scikit-image/scikit-image…
rfezzani May 27, 2020
5138443
Fix corner_peaks doctest
rfezzani May 28, 2020
f9d418c
Vectorize KDtree query
rfezzani May 28, 2020
2162d9b
Update skimage/feature/peak.py
rfezzani Jul 27, 2020
99a4d99
Merge branch 'master' of https://github.com/scikit-image/scikit-image…
rfezzani Jul 27, 2020
8686308
Fix merge conflicts
rfezzani Jul 27, 2020
588c3f6
Merge branch 'master' of https://github.com/scikit-image/scikit-image…
Sep 8, 2020
05c55c7
Merge branch 'master' of github.com:scikit-image/scikit-image into Fi…
Oct 2, 2020
b83786e
Fix docstring
Oct 2, 2020
a2b6a69
Refactor _get_high_intensity_peaks
Oct 7, 2020
d3ae0de
Add ensure_spacing function
Oct 7, 2020
7e492e2
Update _get_threshold
Oct 8, 2020
0f8b7c4
Update _exclude_border
Oct 8, 2020
5767faa
Remove warning in test
Oct 8, 2020
a929d6e
Add docstring for _get_threshold
Oct 8, 2020
abf4a81
Add some comments to ease review
Oct 8, 2020
312cc3b
Merge branch 'master' of github.com:scikit-image/scikit-image into Fi…
Oct 9, 2020
10685b0
Merge branch 'master' of github.com:scikit-image/scikit-image into Fi…
Oct 13, 2020
1bfd56a
Merge branch 'master' into Fix_peack_local_max
rfezzani Oct 19, 2020
a4711a7
Merge branch 'master' into Fix_peack_local_max
rfezzani Oct 22, 2020
e751f13
Merge branch 'master' of https://github.com/scikit-image/scikit-image…
rfezzani Nov 3, 2020
c0bef4c
Fix test_input_labels_unmodified
rfezzani Nov 3, 2020
206c322
Add note about behaviour change in 0.18
jni Nov 6, 2020
73206df
Fix ensure_spacing
rfezzani Nov 6, 2020
2cae084
Revert test_peak.test_empty
rfezzani Nov 6, 2020
4b33a5f
Fix ensure_spacing
rfezzani Nov 6, 2020
3020641
Revert corner_peaks doctest
rfezzani Nov 6, 2020
9321b19
Revert test_disk
rfezzani Nov 6, 2020
b3eeb3a
Simplify test_input_labels_unmodified
rfezzani Nov 6, 2020
7ea85b5
Revert warning msg when footprint.size < 2
rfezzani Nov 6, 2020
739f6ed
Update skimage/_shared/tests/test_coord.py
rfezzani Nov 7, 2020
456053e
Add release notes
rfezzani Nov 7, 2020
eff6e53
Remove warning if footprint.size=1: the warning assertion stands only…
rfezzani Nov 7, 2020
c921138
Merge branch 'Fix_peack_local_max' of github.com:rfezzani/scikit-imag…
rfezzani Nov 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion doc/release/release_dev.rst
Expand Up @@ -73,7 +73,7 @@ Improvements
- ``measure.label`` has been accelerated for boolean input images, by using
``scipy.ndimage``'s implementation for this case (#4945).
- ``util.apply_parallel`` now works with multichannel data (#4927).

- ``skimage.feature.peak_local_max`` supports now any Minkowski distance.


API Changes
Expand All @@ -93,6 +93,8 @@ API Changes
descriptive name.
- The ``level`` parameter of ``measure.find_contours`` is now a keyword
argument, with a default value set to (max(image) - min(image)) / 2.
- ``p_norm`` argument was added to ``skimage.feature.peak_local_max``
to add support for Minkowski distances.


Bugfixes
Expand Down Expand Up @@ -122,6 +124,12 @@ Bugfixes
NaNs are found in the computation (as a result of NaNs in input images).
Before this fix, an incorrect value could be returned where the input images
had NaNs (#4886).
- ``min_distance`` is now enforced for ``skimage.feature.peak_local_max``
(#2592).
- Peak detection in labels is fixed in ``skimage.feature.peak_local_max``
(#4756).
- Input ``labels`` argument renumbering in ``skimage.feature.peak_local_max``
is avoided (#5047).


Deprecations
Expand Down
51 changes: 51 additions & 0 deletions skimage/_shared/coord.py
@@ -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
43 changes: 43 additions & 0 deletions skimage/_shared/tests/test_coord.py
@@ -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