I was reading the docs about template matching with opencv and python and in the last part about template matching with multiple objects, the code detect the 19 coins on the mario image but, is it possible to count the number of objects detected with some function on python like len() or any opencv method?
Here is the code showed on the tutorial:
http://docs.opencv.org/3.1.0/d4/dc6/tutorial_py_template_matching.html
Template Matching Code:
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)
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv2.imwrite('res.png',img_rgb)
And the result is:
Mario Bros & Coins
So, is there any way to count the coins detected on the image and print the number on the terminal?
Something like:
The Template Matching code showed before...
print "Function that detect number of coins with template matching"
>>> 19
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 in zip(*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)
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
def notInList(newObject):
for detectedObject in detectedObjects:
if math.hypot(newObject[0]-detectedObject[0],newObject[1]-detectedObject[1]) < thresholdDist:
return False
return True
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=30
for pt in zip(*loc[::-1]):
if len(detectedObjects) == 0 or 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])
import time
import cv2
import numpy as np
from PIL import ImageGrab
while True:
count = 0
stop = 0
img = ImageGrab.grab()
img_np = np.array(img)
gray = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB)
Template = cv2.imread('image.png' ,0)
w, h = Template.shape[::-1]
res = cv2.matchTemplate(gray, Template, cv2.TM_CCOEFF_NORMED)
threshold = 0.90
loc = np.where(res >= threshold)
font = cv2.FONT_HERSHEY_SIMPLEX
for pt in zip(*loc[::-1]):
cv2.rectangle(frame, pt, (pt[0] + w, pt[1] + h), (0,0,255) ,2)
count = count + 1
print(count)
stop = 1
cv2.imshow('frame',frame)
if (stop == 1):
break