Reputation: 1555
I am trying to follow the tutorial from scikit-image regarding Template Matching (check it here).
Using just this example, I would like to find all matching coins (maxima) in the image, not only this one which gave the highest score. I was thinking about using:
maxima = argrelextrema(result, np.greater)
but the problem is that it finds also very small local maxima, which are just a noise. Is there any way to screen numpy array and find the strongest maxima? Thanks!
Upvotes: 2
Views: 4246
Reputation: 785
To find all the coins the documentation suggests "...you should use a proper peak-finding function." The easiest of these is probably peak_local_max
(as suggested in the comments) which is also from skimage, and has a manual page here. Using some reasonable numbers in the *args gets the peaks out of the response image.
The second comment about the peaks being displaced is also discussed in the documentation
"Note that the peaks in the output of match_template correspond to the origin (i.e. top-left corner) of the template."
One can manually correct for this (by translating the peaks by the side lengths of the template), or you can set the pad_input
bool to True
(source), which as a by-product means that the peaks in the response function line up with the center of the template at the point of maximal overlap.
Combining these two bits into a script we get something like:
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
from skimage.feature import match_template
from skimage.feature import peak_local_max # new import!
image = data.coins()
coin = image[170:220, 75:130]
result = match_template(image, coin,pad_input=True) #added the pad_input bool
peaks = peak_local_max(result,min_distance=10,threshold_rel=0.5) # find our peaks
# produce a plot equivalent to the one in the docs
plt.imshow(result)
# highlight matched regions (plural)
plt.plot(peaks[:,1], peaks[:,0], 'o', markeredgecolor='r', markerfacecolor='none', markersize=10)
Upvotes: 6
Reputation: 1555
I have been digging and found some solution but unfortunately I am not sure if I know what exactly is done in the script. I have slightly modified script found here:
neighborhood_size = 20 #how many pixels
threshold = 0.01 #threshold of maxima?
data_max = filters.maximum_filter(result, neighborhood_size)
maxima = (result == data_max)
data_min = filters.minimum_filter(result, neighborhood_size)
diff = ((data_max - data_min) > threshold)
maxima[diff == 0] = 0
x_image,y_image = [], []
temp_size = coin.shape[0]
labeled, num_objects = ndimage.label(maxima)
slices = ndimage.find_objects(labeled)
x, y = [], []
for dy,dx in slices:
x_center = (dx.start + dx.stop - 1)/2
x.append(x_center)
y_center = (dy.start + dy.stop - 1)/2
y.append(y_center)
fig, (raw,found) = plt.subplots(1,2)
raw.imshow(image,cmap=plt.cm.gray)
raw.set_axis_off()
found.imshow(result)
found.autoscale(False)
found.set_axis_off()
plt.plot(x,y, 'ro')
plt.show()
I also realized, that coordinates of found peaks are shifted in comparison to raw image. I think the difference comes from the template size. I will update when I will find out more.
EDIT: with slight code modification I was able also to find places on the input image:
x_image_center = (dx.start + dx.stop - 1 + temp_size) / 2
x_image.append(x_image_center)
y_image_center = (dy.start + dy.stop - 1 + temp_size) / 2
y_image.append(y_image_center)
Upvotes: 0