How to count the number of objects detected with Template Matching?

折月煮酒 提交于 2019-12-21 06:09:02

问题


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

回答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 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)



回答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

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])    



回答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 = 0
    for match in matches:
        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]:
        return 0

    if top_right1[1] < bottom_left2[1] or bottom_left1[1] > top_right2[1]:
        return 0

    return 1



回答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 in sorted(zip(*loc[::-1])):
        if sqrt(abs(last_pt[0]-pt[0])**2 + abs(last_pt[0]-pt[0])**2) < threshold*min([h, w]):
            continue
        else:
            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



回答5:


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


来源:https://stackoverflow.com/questions/40824445/how-to-count-the-number-of-objects-detected-with-template-matching

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!