Skip to content

Commit e822781

Browse files
committed
Deprecate threshold_(abs|rel) in favor of threshold
Note, that `threshold_rel=0` is equivalent to `threshold=0`, since `image.max() * 0 == 0`.
1 parent bad7afe commit e822781

7 files changed

Lines changed: 155 additions & 99 deletions

File tree

TODO.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Version 0.27
2727
Version 0.29
2828
------------
2929
* Complete deprecation of `threshold_rel` in `skimage.feature.blob_*` functions
30+
* Complete deprecation of `threshold_abs` and `threshold_rel` in
31+
`skimage.feature.peak_local_max`. Remove `skimage.feature.peak._get_threshold`.
3032

3133
Other
3234
-----

src/skimage/feature/blob.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
)
1515
from ..transform import integral_image
1616
from ._hessian_det_appx import _hessian_matrix_det
17-
from .peak import peak_local_max
17+
from .peak import peak_local_max, _get_threshold
1818

1919
# This basic blob detection algorithm is based on:
2020
# http://www.cs.utah.edu/~jfishbau/advimproc/project1/ (04.04.2013)
@@ -381,8 +381,9 @@ def blob_dog(
381381
The radius of each blob is approximately :math:`\sqrt{2}\sigma` for
382382
a 2-D image and :math:`\sqrt{3}\sigma` for a 3-D image.
383383
"""
384-
if threshold_rel is DEPRECATED:
385-
threshold_rel = None
384+
if threshold_rel is not DEPRECATED:
385+
# Maintain backwards-compatible behavior of `threshold_rel` during deprecation
386+
threshold = _get_threshold(image, threshold, threshold_rel)
386387

387388
image = _rescale_value_range(image, mode=prescale)
388389
float_dtype = _supported_float_type(image.dtype)
@@ -428,8 +429,7 @@ def blob_dog(
428429
exclude_border = _format_exclude_border(image.ndim, exclude_border)
429430
local_maxima = peak_local_max(
430431
dog_image_cube,
431-
threshold_abs=threshold,
432-
threshold_rel=threshold_rel,
432+
threshold=threshold,
433433
exclude_border=exclude_border,
434434
footprint=np.ones((3,) * (image.ndim + 1)),
435435
)
@@ -595,8 +595,9 @@ def blob_log(
595595
The radius of each blob is approximately :math:`\sqrt{2}\sigma` for
596596
a 2-D image and :math:`\sqrt{3}\sigma` for a 3-D image.
597597
"""
598-
if threshold_rel is DEPRECATED:
599-
threshold_rel = None
598+
if threshold_rel is not DEPRECATED:
599+
# Maintain backwards-compatible behavior of `threshold_rel` during deprecation
600+
threshold = _get_threshold(image, threshold, threshold_rel)
600601

601602
image = _rescale_value_range(image, mode=prescale)
602603
float_dtype = _supported_float_type(image.dtype)
@@ -632,8 +633,7 @@ def blob_log(
632633
exclude_border = _format_exclude_border(image.ndim, exclude_border)
633634
local_maxima = peak_local_max(
634635
image_cube,
635-
threshold_abs=threshold,
636-
threshold_rel=threshold_rel,
636+
threshold=threshold,
637637
exclude_border=exclude_border,
638638
footprint=np.ones((3,) * (image.ndim + 1)),
639639
)
@@ -794,8 +794,9 @@ def blob_doh(
794794
this method can't be used for detecting blobs of radius less than `3px`
795795
due to the box filters used in the approximation of Hessian Determinant.
796796
"""
797-
if threshold_rel is DEPRECATED:
798-
threshold_rel = None
797+
if threshold_rel is not DEPRECATED:
798+
# Maintain backwards-compatible behavior of `threshold_rel` during deprecation
799+
threshold = _get_threshold(image, threshold, threshold_rel)
799800

800801
check_nD(image, 2)
801802

@@ -817,8 +818,7 @@ def blob_doh(
817818

818819
local_maxima = peak_local_max(
819820
image_cube,
820-
threshold_abs=threshold,
821-
threshold_rel=threshold_rel,
821+
threshold=threshold,
822822
exclude_border=False,
823823
footprint=np.ones((3,) * image_cube.ndim),
824824
)

src/skimage/feature/corner.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ..util import img_as_float
1313
from ._hessian_det_appx import _hessian_matrix_det
1414
from .corner_cy import _corner_fast, _corner_moravec, _corner_orientations
15-
from .peak import peak_local_max
15+
from .peak import peak_local_max, _get_threshold
1616
from .util import _prepare_grayscale_input_2D, _prepare_grayscale_input_nD
1717

1818

@@ -1204,12 +1204,14 @@ def corner_peaks(
12041204
if np.isinf(num_peaks_per_label):
12051205
num_peaks_per_label = None
12061206

1207+
# Maintain backwards-compatible behavior of `threshold_rel` during deprecation
1208+
threshold = _get_threshold(image, threshold_abs, threshold_rel)
1209+
12071210
# Get the coordinates of the detected peaks
12081211
coords = peak_local_max(
12091212
image,
12101213
min_distance=min_distance,
1211-
threshold_abs=threshold_abs,
1212-
threshold_rel=threshold_rel,
1214+
threshold=threshold,
12131215
exclude_border=exclude_border,
12141216
num_peaks=None, # Limiting to `num_peaks` is done in this function
12151217
footprint=footprint,

src/skimage/feature/peak.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,52 @@
66
from .. import measure
77
from ..util import PendingSkimage2Change
88
from .._shared._warnings import warn_external
9+
from .._shared.utils import deprecate_parameter, DEPRECATED
910

1011
import skimage2 as ski2
1112

1213

14+
def _get_threshold(image, threshold_abs, threshold_rel):
15+
"""Return the threshold value according to an absolute and a relative
16+
value.
17+
18+
"""
19+
threshold = threshold_abs if threshold_abs is not None else image.min()
20+
21+
if threshold_rel is not None:
22+
threshold = max(threshold, threshold_rel * image.max())
23+
24+
return threshold
25+
26+
27+
@deprecate_parameter(
28+
"threshold_rel",
29+
start_version="0.27",
30+
stop_version="0.29",
31+
template="Parameter `{deprecated_name}` is deprecated since version "
32+
"{deprecated_version} and will be removed in {changed_version} (or later). "
33+
"To avoid this warning, use `threshold=image.max() * threshold_rel` instead. "
34+
"For more details, see the documentation of `{func_name}`.",
35+
)
36+
@deprecate_parameter(
37+
"threshold_abs",
38+
new_name="threshold",
39+
start_version="0.27",
40+
stop_version="0.29",
41+
)
1342
def peak_local_max(
1443
image,
1544
min_distance=1,
16-
threshold_abs=None,
17-
threshold_rel=None,
45+
threshold_abs=DEPRECATED,
46+
threshold_rel=DEPRECATED,
1847
exclude_border=True,
1948
num_peaks=None,
2049
footprint=None,
2150
labels=None,
2251
num_peaks_per_label=None,
2352
p_norm=np.inf,
53+
*,
54+
threshold=None,
2455
):
2556
"""Find peaks in an image as coordinate list.
2657
@@ -43,12 +74,16 @@ def peak_local_max(
4374
min_distance : int, optional
4475
The minimal allowed distance separating peaks. To find the
4576
maximum number of peaks, use `min_distance=1`.
46-
threshold_abs : float or None, optional
47-
Minimum intensity of peaks. By default, the absolute threshold is
48-
the minimum intensity of the image.
49-
threshold_rel : float or None, optional
50-
Minimum intensity of peaks, calculated as
51-
``max(image) * threshold_rel``.
77+
threshold_abs : DEPRECATED
78+
79+
.. deprecated:: 0.27
80+
Use `threshold` instead.
81+
82+
threshold_rel : DEPRECATED
83+
84+
.. deprecated:: 0.27
85+
Use `threshold=image.max() * threshold_rel` instead.
86+
5287
exclude_border : int, tuple of ints, or bool, optional
5388
Control peak detection close to the border of `image`.
5489
@@ -79,6 +114,9 @@ def peak_local_max(
79114
A finite large p may cause a ValueError if overflow can occur.
80115
``inf`` corresponds to the Chebyshev distance and 2 to the
81116
Euclidean distance.
117+
threshold : float, optional
118+
Minimum intensity of peaks. By default, the absolute threshold is
119+
the minimum intensity of the image.
82120
83121
Returns
84122
-------
@@ -176,6 +214,11 @@ def peak_local_max(
176214
category=FutureWarning,
177215
)
178216

217+
assert threshold_abs in (DEPRECATED, deprecate_parameter.DEPRECATED_GOT_VALUE)
218+
if threshold_rel is not DEPRECATED:
219+
# Maintain backwards-compatible behavior of `threshold_rel` during deprecation
220+
threshold = _get_threshold(image, threshold, threshold_rel)
221+
179222
if exclude_border is False:
180223
exclude_border = 0
181224
elif exclude_border is True:
@@ -184,8 +227,7 @@ def peak_local_max(
184227
coordinates = ski2.feature.peak_local_max(
185228
image,
186229
min_distance=min_distance,
187-
threshold_abs=threshold_abs,
188-
threshold_rel=threshold_rel,
230+
threshold=threshold,
189231
exclude_border=exclude_border,
190232
num_peaks=num_peaks,
191233
footprint=footprint,

src/skimage2/feature/_peaks.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,6 @@ def _exclude_border(label, border_width):
6262
return label
6363

6464

65-
def _get_threshold(image, threshold_abs, threshold_rel):
66-
"""Return the threshold value according to an absolute and a relative
67-
value.
68-
69-
"""
70-
threshold = threshold_abs if threshold_abs is not None else image.min()
71-
72-
if threshold_rel is not None:
73-
threshold = max(threshold, threshold_rel * image.max())
74-
75-
return threshold
76-
77-
7865
def _validate_exclude_border(exclude_border, *, ndim):
7966
"""Return border_width values relative to a min_distance if requested."""
8067

@@ -110,8 +97,7 @@ def peak_local_max(
11097
image,
11198
*,
11299
min_distance=1,
113-
threshold_abs=None,
114-
threshold_rel=None,
100+
threshold=None,
115101
exclude_border=0,
116102
num_peaks=None,
117103
footprint=None,
@@ -134,12 +120,9 @@ def peak_local_max(
134120
min_distance : int, optional
135121
The minimal allowed distance separating peaks. To find the
136122
maximum number of peaks, use `min_distance=1`. See also `p_norm`.
137-
threshold_abs : float, optional
138-
Minimum intensity of peaks. By default, the absolute threshold is
139-
the minimum intensity of the image.
140-
threshold_rel : float, optional
141-
Minimum intensity of peaks, calculated as
142-
``max(image) * threshold_rel``.
123+
threshold : float, optional
124+
Minimum intensity of peaks. By default, the threshold is the minimum
125+
intensity of the image.
143126
exclude_border : int or tuple of (int, ...), optional
144127
Control peak detection close to the border of `image`.
145128
@@ -225,7 +208,8 @@ def peak_local_max(
225208

226209
border_width = _validate_exclude_border(exclude_border, ndim=image.ndim)
227210

228-
threshold = _get_threshold(image, threshold_abs, threshold_rel)
211+
if threshold is None:
212+
threshold = image.min()
229213

230214
if footprint is None:
231215
size = 2 * min_distance + 1

tests/skimage/feature/test_corner.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -460,19 +460,19 @@ def test_square_image():
460460

461461
# Harris
462462
results = peak_local_max(
463-
corner_harris(im, method='k'), min_distance=10, threshold_rel=0
463+
corner_harris(im, method='k'), min_distance=10, threshold=0
464464
)
465465
# interest at corner
466466
assert len(results) == 1
467467

468468
results = peak_local_max(
469-
corner_harris(im, method='eps'), min_distance=10, threshold_rel=0
469+
corner_harris(im, method='eps'), min_distance=10, threshold=0
470470
)
471471
# interest at corner
472472
assert len(results) == 1
473473

474474
# Shi-Tomasi
475-
results = peak_local_max(corner_shi_tomasi(im), min_distance=10, threshold_rel=0)
475+
results = peak_local_max(corner_shi_tomasi(im), min_distance=10, threshold=0)
476476
# interest at corner
477477
assert len(results) == 1
478478

@@ -510,23 +510,23 @@ def test_noisy_square_image():
510510
im = im + rng.uniform(size=im.shape) * 0.2
511511

512512
# Moravec
513-
results = peak_local_max(corner_moravec(im), min_distance=10, threshold_rel=0)
513+
results = peak_local_max(corner_moravec(im), min_distance=10, threshold=0)
514514
# undefined number of interest points
515515
assert results.any()
516516

517517
# Harris
518518
results = peak_local_max(
519-
corner_harris(im, method='k'), min_distance=10, threshold_rel=0
519+
corner_harris(im, method='k'), min_distance=10, threshold=0
520520
)
521521
assert len(results) == 1
522522
results = peak_local_max(
523-
corner_harris(im, method='eps'), min_distance=10, threshold_rel=0
523+
corner_harris(im, method='eps'), min_distance=10, threshold=0
524524
)
525525
assert len(results) == 1
526526

527527
# Shi-Tomasi
528528
results = peak_local_max(
529-
corner_shi_tomasi(im, sigma=1.5), min_distance=10, threshold_rel=0
529+
corner_shi_tomasi(im, sigma=1.5), min_distance=10, threshold=0
530530
)
531531
assert len(results) == 1
532532

@@ -539,11 +539,11 @@ def test_squared_dot():
539539
# Moravec fails
540540

541541
# Harris
542-
results = peak_local_max(corner_harris(im), min_distance=10, threshold_rel=0)
542+
results = peak_local_max(corner_harris(im), min_distance=10, threshold=0)
543543
assert (results == np.array([[6, 6]])).all()
544544

545545
# Shi-Tomasi
546-
results = peak_local_max(corner_shi_tomasi(im), min_distance=10, threshold_rel=0)
546+
results = peak_local_max(corner_shi_tomasi(im), min_distance=10, threshold=0)
547547
assert (results == np.array([[6, 6]])).all()
548548

549549

@@ -580,7 +580,7 @@ def test_subpix_edge(dtype):
580580
img[:25, :25] = 255
581581
img[25:, 25:] = 255
582582
corner = peak_local_max(
583-
corner_harris(img), min_distance=10, threshold_rel=0, num_peaks=1
583+
corner_harris(img), min_distance=10, threshold=0, num_peaks=1
584584
)
585585
subpix = corner_subpix(img, corner)
586586
assert subpix.dtype == _supported_float_type(dtype)
@@ -591,7 +591,7 @@ def test_subpix_dot():
591591
img = np.zeros((50, 50))
592592
img[25, 25] = 255
593593
corner = peak_local_max(
594-
corner_harris(img), min_distance=10, threshold_rel=0, num_peaks=1
594+
corner_harris(img), min_distance=10, threshold=0, num_peaks=1
595595
)
596596
subpix = corner_subpix(img, corner)
597597
assert_array_equal(subpix[0], (25, 25))
@@ -604,7 +604,7 @@ def test_subpix_no_class():
604604

605605
img[25, 25] = 1e-10
606606
corner = peak_local_max(
607-
corner_harris(img), min_distance=10, threshold_rel=0, num_peaks=1
607+
corner_harris(img), min_distance=10, threshold=0, num_peaks=1
608608
)
609609
subpix = corner_subpix(img, corner)
610610
assert_array_equal(subpix[0], (np.nan, np.nan))
@@ -642,9 +642,7 @@ def test_num_peaks():
642642

643643
for i in range(20):
644644
n = np.random.randint(1, 21)
645-
results = peak_local_max(
646-
img_corners, min_distance=10, threshold_rel=0, num_peaks=n
647-
)
645+
results = peak_local_max(img_corners, min_distance=10, threshold=0, num_peaks=n)
648646
assert results.shape[0] == n
649647

650648

0 commit comments

Comments
 (0)