Opencv matchTemplate and np.where(): keep only unique values

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:

enter image description here

Template:

enter image description here

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:

enter image description here

Final Masked Correlation Image showing masked regions:

enter image description here

Resulting Matches on Input:

enter image description here

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

Leave a Comment