This is an alternate method for multi-template matching in Python/OpenCV to that provided at https://www.pyimagesearch.com/2021/03/29/multi-template-matching-with-opencv/. I borrow the images from that example.
My method simply consists of iterative peak suppression (via masking the template matching correlation image) until a threshold is reached in the match value or a given number of matches is achieved. I set the maximum number of matches to 10 and the stopping threshold on the match value to 0.95 (out of 1 as a perfect match). I also set the radius of the masked region to 1/4 of the size of the template largest dimension.
Input:
Template:
import cv2
import numpy as np
# read image
img = cv2.imread('8diamond.png')
# read template
tmplt = cv2.imread('1diamond.png')
hh, ww, cc = tmplt.shape
# set arguments
match_thresh = 0.95 # stopping threshold for match value
num_matches = 10 # stopping threshold for number of matches
match_radius = max(hh,ww)//4 # approx radius of match peaks
# get correlation surface from template matching
corrimg = cv2.matchTemplate(img,tmplt,cv2.TM_CCORR_NORMED)
hc, wc = corrimg.shape
# get locations of all peaks higher than match_thresh for up to num_matches
imgcopy = img.copy()
corrcopy = corrimg.copy()
for i in range(1, num_matches):
# get max value and location of max
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(corrcopy)
x1 = max_loc[0]
y1 = max_loc[1]
x2 = x1 + ww
y2 = y1 + hh
loc = str(x1) + "," + str(y1)
if max_val > match_thresh:
print("match number:", i, "match value:", max_val, "match x,y:", loc)
# draw draw blue bounding box to define match location
cv2.rectangle(imgcopy, (x1,y1), (x2,y2), (255,0,0), 2)
# draw black filled circle over copy of corrimg
cv2.circle(corrcopy, (x1,y1), match_radius, 0, -1)
i = i + 1
else:
break
# save results
# power of 4 exaggeration of correlation image to emphasize peaks
cv2.imwrite('8diamond_multi_template_corr.png', (255*cv2.pow(corrimg,4)).clip(0,255).astype(np.uint8))
cv2.imwrite('8diamond_multi_template_corr_masked.png', (255*cv2.pow(corrcopy,4)).clip(0,255).astype(np.uint8))
cv2.imwrite('8diamond_multi_template_match.png', imgcopy)
# show results
# power of 4 exaggeration of correlation image to emphasize peaks
cv2.imshow('image', img)
cv2.imshow('template', tmplt)
cv2.imshow('corr', cv2.pow(corrimg,4))
cv2.imshow('corr masked', cv2.pow(corrcopy,4))
cv2.imshow('result', imgcopy)
cv2.waitKey(0)
cv2.destroyAllWindows()
Correlation Image from Template Matching:
Final Masked Correlation Image showing masked regions:
Resulting Matches on Input:
Textual Output:
match number: 1 match value: 0.9954221844673157 match x,y: 70,84
match number: 2 match value: 0.9926357865333557 match x,y: 112,132
match number: 3 match value: 0.9903871417045593 match x,y: 157,89
match number: 4 match value: 0.9901180267333984 match x,y: 67,173
match number: 5 match value: 0.9888120889663696 match x,y: 63,261
match number: 6 match value: 0.9862607717514038 match x,y: 108,219
match number: 7 match value: 0.9856082797050476 match x,y: 152,178
match number: 8 match value: 0.9837512969970703 match x,y: 146,261