Comments (10)
I agree, it is quite elegant actually, but does it ensure that if multiple peaks are found within min_distance it returns only the one with the largest height?
Yes, hopefully 🙂 ...
Ok, then we agree that the problem is when the peaks have the same height :D. In this case, with min_distance, peak_local_max returns only one peak, so I would expect that footprint does the same, no?
footprint
only defines the region where to search for local minima around each point, and min_distance
will "control" the number of detected peaks: one may want to search for local maxima within a disk of radius 5 around each point, but impose a minimum distance of 20 between each peak...
from scikit-image.
It sounds like what you need is for min_distance
to accept a value along each axis.
The reason why the footprint picks up both peaks is because they are exactly the same height. If, instead, the one was slightly higher than the other, it would be rejected.
I'm not sure what the best way would be to fairly break this tie, otherwise.
Here's code for differing peak heights, that also plots the footprint circle on the input:
import numpy as np
import skimage as ski
import matplotlib.pyplot as plt
def gauss_2D_peak(xx, yy, xc, yc, sigma):
xx = xx - xc
yy = yy - yc
gauss_x = np.exp(-(xx**2)/(2*(sigma**2)))
gauss_y = np.exp(-(yy**2)/(2*(sigma**2)))
gauss_yx = gauss_x*gauss_y
return gauss_yx
Y = 128
X = 128
yy, xx = np.ogrid[0:Y, 0:X]
sigma = 3
yc, xc = 64, 64
dist = 8
xc1 = round(xc - dist/2)
xc2 = round(xc + dist/2)
img = (
gauss_2D_peak(xx, yy, xc1, yc, sigma)
+ 1.1 * gauss_2D_peak(xx, yy, xc2, yc, sigma)
)
min_dist = 40
radius_footprint = min_dist/2
wh = int(np.ceil(radius_footprint))
y, x = np.ogrid[-wh:wh+1, -wh:wh+1]
footprint = (x**2 + y**2)/(radius_footprint**2) <= 1
peaks = ski.feature.peak_local_max(img, footprint=footprint)
print(peaks)
fig, ax = plt.subplots(1, 3)
ax[0].imshow(img)
ax[0].plot(peaks[:, 1], peaks[:, 0], 'r.')
ax[1].plot(img.max(axis=0))
ax[2].imshow(footprint)
for coord in peaks[:, ::-1]:
circle = plt.Circle(coord, radius_footprint, edgecolor='red', fill=False)
ax[0].add_patch(circle)
plt.show()
from scikit-image.
Thank you very much for the speedy reply!
I did not know that, very interesting. I don't know how feasible it is, but should an arbitrary peak be returned like with min_distance
parameter?
It sounds like what you need is for min_distance to accept a value along each axis.
That is not possible at the moment right?
from scikit-image.
No, unfortunately not. But feel free to file a feature request for either of the above! (Or, even better, if you have a moment to glance at the code to see how hard it would be to add: https://github.com/scikit-image/scikit-image/blob/main/skimage/feature/peak.py#L115).
from scikit-image.
Perfect, I was already looking into the code to check how this could be implemented. I think allowing one distance per axis would be cool. Thank you very much!
from scikit-image.
Great, thanks @ElpadoCan! Please file an issue for that, whether or not you intend to work on it yourself.
from scikit-image.
I think that what you observe is the expected behavior!
As the doc describes, footprint
defines the local region within which to search for peaks at every point in image, and min_distance
the minimal allowed distance separating peaks.
min_distance
is the main parameter to control the number of peaks and in the code snippet you provided, it is set to its default value 1.
I modified the example you provided to test different values of min_distance
:
import numpy as np
import skimage.draw
import skimage.feature
import matplotlib.pyplot as plt
def gauss_2D_peak(xx, yy, xc, yc, sigma):
xx = xx - xc
yy = yy - yc
gauss_x = np.exp(-(xx**2)/(2*(sigma**2)))
gauss_y = np.exp(-(yy**2)/(2*(sigma**2)))
gauss_yx = gauss_x*gauss_y
return gauss_yx
Y = 128
X = 128
yy, xx = np.ogrid[0:Y, 0:X]
sigma = 3
yc, xc = 64, 64
dist = 8
xc1 = round(xc - dist/2)
xc2 = round(xc + dist/2)
img = (
gauss_2D_peak(xx, yy, xc1, yc, sigma)
+ gauss_2D_peak(xx, yy, xc2, yc, sigma)
)
min_dist = 20
radius_footprint = min_dist/2
wh = int(np.ceil(radius_footprint))
y, x = np.ogrid[-wh:wh+1, -wh:wh+1]
footprint = (x**2 + y**2)/(radius_footprint**2) <= 1
fig, ax_list = plt.subplots(1, 6, figsize=(12, 3))
for d, ax in zip([1, 5, 10, 20], ax_list):
peaks = skimage.feature.peak_local_max(img, min_distance=d,
footprint=footprint)
print(f"min_distance = {d :2d}: {len(peaks)} peak(s) detected")
ax.imshow(img)
ax.plot(peaks[:, 1], peaks[:, 0], 'r.')
ax.set_title(f"min_distance={d}")
ax.set_axis_off()
ax_list[-2].plot(img.max(axis=0))
ax_list[-2].set_title("profile")
ax_list[-1].imshow(footprint)
ax_list[-1].set_title("footprint")
ax_list[-1].set_axis_off()
fig.tight_layout()
plt.show()
To obtain
min_distance = 1: 2 peak(s) detected
min_distance = 5: 2 peak(s) detected
min_distance = 10: 1 peak(s) detected
min_distance = 20: 1 peak(s) detected
from scikit-image.
Hi @rfezzani,
Thanks for looking into this! And yes, I agree, I know that min_distance
is the right parameter to use, but my images are anisotropic and I would ideally need one distance per axis of the input image. I opened #7318 to discuss this implementation.
Regarding the footprint
instead, I admit that I don't fully understand how it works. If you see this tutorial on watershed, the footprint
is set to a square with 3 pixels side and I think they do it to avoid detecting multiple peaks. That means that footprint
should have similar effects to min_distance
, right?
You can also see it in @stefanv comment that if you have peaks with different heights, then we get only one peak with a large enough footprint (despite min_distance=1
) .
My expectation was that only one peak can be detected within each footprint, but that does not seem to be the case. If you have more insights, please share them with me because I'm very curious, thanks!
Cheers,
Francesco
from scikit-image.
Concerning the anisotropy of your data, I think that the approach proposed by @jni #7318 (comment) is the simplest way to implement this feature.
In the watershed
tutorial, the definition of footprint
is useless as it is set to its default value (please see the code).
In the solution proposed by @stefanv, if you scale up one of the two peaks, it becomes the local maximum within the region defined by footprint
around the other peak, and thus becomes the only detected peak. But I don't know how this can be implemented with real world data :/.
from scikit-image.
Concerning the anisotropy of your data, I think that the approach proposed by @jni #7318 (comment) is the simplest way to implement this feature.
I agree, it is quite elegant actually, but does it ensure that if multiple peaks are found within min_distance
it returns only the one with the largest height?
In the
watershed
tutorial, the definition offootprint
is useless as it is set to its default value (please see the code).
Oh yeah, you are right, thanks for clarifying this!
In the solution proposed by @stefanv, if you scale up one of the two peaks, it becomes the local maximum within the region defined by
footprint
around the other peak, and thus becomes the only detected peak. But I don't know how this can be implemented with real world data :/.
Ok, then we agree that the problem is when the peaks have the same height :D. In this case, with min_distance
, peak_local_max returns only one peak, so I would expect that footprint does the same, no?
from scikit-image.
Related Issues (20)
- test_unsharp_masking_output_type_and_shape fails on non-x86 architectures HOT 9
- H&E and Residual, rather than HED HOT 5
- Invalid no-name-in-module from pylint on scikit-image>=0.19.0 using filters module HOT 3
- libatlas 3.10.3 related failures on debian
- Discrepancy of skimage.filters.frangi output between 0.19.3 and 0.22.0 HOT 2
- Adding `spacing` to `extra_properties`'s possible arguments in `regionprops` HOT 3
- CI fails on MacOS with "clang cannot compile programs" HOT 2
- ORB test points file should be read in x-y and not r-c
- Typo in `skimage.measure.find_contours` HOT 3
- Vulnerability: code injection HOT 9
- The Skeletonize function output wrong figure. HOT 3
- Docs mention non-existing ASV benchmarks HOT 2
- `morphology.skel` modifies input image HOT 1
- moments_weighted_normalized causes warning "invalid value encountered in double_scalars" HOT 2
- rgb2lab([1,1,1]) returns [0.,0.,0.] HOT 2
- Rolling ball algorithm scales badly with radius HOT 10
- [0.23.2] test_active_contour_model.py fails on mips64el
- Pick and place object detection for nuts and bolts HOT 2
- Unexpected result after excute morphology.skeletonize HOT 3
- Testsuite crashes with 'Bus Error' on sparc64 due to unaligned access HOT 23
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scikit-image.