skimage peak local max finds multiple spots in close proximity due to image impurities
I have an image that looks like this, with some larger impurities/overexposed spots. It generally doesn't matter if they're detected, as measurements are time resolved, so they'll be removed later on.
However, I'm interested in as many as the small dots as possible - as fast as possible. skimage.feature.peak_local_max
does a really good job, and is very easy to use on different data, because there's no need to play around much with intensity scaling.
The problem is though, that large spots for some reason give very strong positives.
import skimage.io import skimage.feature import skimage.morphology from matplotlib.collections import PatchCollection import matplotlib.pyplot as plt def plotRoi(spots, img_ax, color, radius): patches = [] for spot in spots: y, x = spot c = plt.Circle((x, y), radius) patches.append(c) img_ax.add_collection(PatchCollection(patches, facecolors = "None", edgecolors = color, alpha = 0.3, linewidths = 1)) img = skimage.io.imread("/Path/to/img.png") img = img[:,:,0] fig, ax = plt.subplots() ax.imshow(img, cmap = "Greys") spots = skimage.feature.peak_local_max(img, min_distance = 0, exclude_border = True, num_peaks = 2000) plotRoi(spots, ax, "red", radius = 10) plt.show()
And searching for thousands of spots in some images lead to a large number of local maxima being pretty much on top of each other. Is there a way to avoid this, e.g. by applying a filter on image loading, as I would prefer not to move to a slower type of peak fitting?
2 Answers
The problem is that there are peaks in regions of pixels with exactly the same value. One way to solve this is to merge the peaks to the centre of mass these regions.
Below I recreate the problem and solve it as stated.
import numpy as np import matplotlib.pyplot as plt from skimage.feature import peak_local_max from scipy.ndimage.measurements import center_of_mass, label # Generate test data with two peaks, one of which consists of two pixels of equal value image = np.zeros((11,11),dtype=np.uint8) image[5,3] = 128 image[5,2] = 255 image[5,7:9] = 255 image[6,8] = 128 # Finding peaks normally; results in three peaks peaks = peak_local_max(image) # Find peaks and merge equal regions; results in two peaks is_peak = peak_local_max(image, indices=False) # outputs bool image labels = label(is_peak)[0] merged_peaks = center_of_mass(is_peak, labels, range(1, np.max(labels)+1)) merged_peaks = np.array(merged_peaks) # Visualize the results fig,(ax1,ax2)=plt.subplots(1,2) ax1.imshow(image.T,cmap='gray') ax1.plot(peaks[:,0],peaks[:,1],'ro') ax2.imshow(image.T,cmap='gray') ax2.plot(merged_peaks[:,0],merged_peaks[:,1],'ro')
Result:
3This doesn't fix the problem for me. It merges some peaks but not others. Furthermore, peak merging should be part of peak_local_max. Otherwise the min_distance definition is incorrect.
ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJobGpuZ2eAc4OOrKKipZGcsm68xJqiZqSfmK6tecyar2aemaOxtHnMrqOtoaChsm6%2Fz6irrGWZo3qkuM6snGaooqTFqrnIrbBmnKWaerW7jKKkmp%2BVYrauvNQ%3D