How To Count The Number Of Objects Detected With Template Matching?
Solution 1:
I found a suitable solution (for my application) in counting unique matches as Ulrich suggested. It's not ideal, but playing with the "sensitivity" normally yields results within +/- 2% for my application.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv2.imread('mario.png')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.png',0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)
f = set()
for pt inzip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
sensitivity = 100
f.add((round(pt[0]/sensitivity), round(pt[1]/sensitivity)))
cv2.imwrite('res.png',img_rgb)
found_count = len(f)
Solution 2:
I used a list to store the very first (x,y) of many same object detections. Then for every (x,y) in the found detections(there must be many detections on the one same object), I calculate the distance between the new (x,y) and every points in the list. If the distance is large enough, it must be the first found of a new detection. Then I put the new (x,y)to the list. It is stupid but really works.
The purpose is to remove the points nearby the (x,y) of the first detection of an object and keep only one point of that 'group', then iterate all the points in loc to locate more 'groups' and find one and the only one point in every group.
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
defnotInList(newObject):
for detectedObject in detectedObjects:
if math.hypot(newObject[0]-detectedObject[0],newObject[1]-detectedObject[1]) < thresholdDist:
returnFalsereturnTrue
img_rgb = cv2.imread("7.jpg")
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread("face.jpg",0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.85
loc = np.where( res >= threshold)
detectedObjects=[]
thresholdDist=30for pt inzip(*loc[::-1]):
iflen(detectedObjects) == 0or notInList(pt):
detectedObjects.append(pt)
cellImage=img_rgb[pt[1]:pt[1]+h, pt[0]:pt[0]+w]
cv2.imwrite("results/"+str(pt[1])+"_"+str(pt[0])+".jpg",cellImage,
[int(cv2.IMWRITE_JPEG_QUALITY), 50])
Solution 3:
i made a list of all the matches and for every new match i check if there's an intersection with the bounding box any of the matches in the list:
res = cv.matchTemplate(image,template,cv.TM_CCOEFF_NORMED)
threshold =0.5
loc = np.where(res >= threshold)
matches= []
for pt in zip(*loc[::-1]):
intersection=0formatchinmatches:
if intersected(match, (match[0] + w, match[1] + h), pt, (pt[0] + w, pt[1] + h)):
intersection=1
break
if intersection==0:
matches.append(pt)
rect = cv.rectangle(image, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
this is the code to check for an intersection:
def intersected(bottom_left1, top_right1, bottom_left2, top_right2):
if top_right1[0] < bottom_left2[0] or bottom_left1[0] > top_right2[0]:
return0if top_right1[1] < bottom_left2[1] or bottom_left1[1] > top_right2[1]:
return0return1
Solution 4:
To anyone still wondering: It's way easier to sort the list "zip(*loc[::-1})" first.
For i.e my script returned found results like this:
(580, 822)
(871, 822)
(1017, 822)
(434, 823)
(726, 823)
(871, 823)
(1017, 823)
7
You'll notice that there's multiple duplication but not just in order. Just simply sorting this with "sorted(zip(*loc[::-1]))" will now make getting distance a piece of cake, by simply calculating adjacent 2 point and check distance on every loop.
By adding condition in loop and check if current point has smaller distance than desired one will get job done nicely. I never learned python properly so I'm not sure this is efficient way or not.. At least this worked for my use case. You can check it out below.
Source code (Github) / Test Result (Imgur)
Example Code:
img = np.array( *YOUR_SCREENSHOT_HERE* )
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread( *TARGET_IMAGE_HERE* , 0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(res >= precision)
count = 0
last_pt = [0, 0] # Set this negative if target image is at top-left corner.for pt insorted(zip(*loc[::-1])):
if sqrt(abs(last_pt[0]-pt[0])**2 + abs(last_pt[0]-pt[0])**2) < threshold*min([h, w]):
continueelse:
last_pt = pt
print(pt)
count = count + 1
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
cv2.imwrite('res.png', img)
return count
Solution 5:
That is how I did it:
loc = np.where( res >= threshhold)
print(len(loc[0])) #match occurences count
loc is 2D array.
Post a Comment for "How To Count The Number Of Objects Detected With Template Matching?"