如何计算模板匹配检测到的对象数量?

Posted

技术标签:

【中文标题】如何计算模板匹配检测到的对象数量?【英文标题】:How to count the number of objects detected with Template Matching? 【发布时间】:2017-04-11 00:04:37 【问题描述】:

我正在阅读关于模板匹配与 opencv 和 python 的文档,在最后一部分关于模板匹配多个对象,代码检测马里奥图像上的 19 个硬币但是,是否可以计算检测到的对象数量python 上的一些函数,如 len() 或任何 opencv 方法?

这是教程中显示的代码: http://docs.opencv.org/3.1.0/d4/dc6/tutorial_py_template_matching.html

模板匹配代码:

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)

结果是: Mario Bros & Coins

那么,有没有什么方法可以计算在图像上检测到的硬币并在终端上打印数字呢? 比如:

The Template Matching code showed before...

print "Function that detect number of coins with template matching"
>>> 19

【问题讨论】:

向我们展示您迄今为止所做的尝试。 查看类似的question。但是,那里的答案没有为问题中的“65 vs. 19”问题提供适当的解决方案。一个合理可靠的解决方案是不计算与前一场比赛非常接近的比赛。 (如果您提供输入图像,人们会更容易为您编写代码。) 【参考方案1】:

按照 Ulrich 的建议,我找到了一个合适的解决方案(适用于我的应用程序)来计算唯一匹配项。这并不理想,但使用“灵敏度”通常会在我的应用程序中产生 +/- 2% 的结果。

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】:

我使用一个列表来存储许多相同对象检测的第一个 (x,y)。然后对于找到的检测中的每个 (x,y)(在同一个对象上必须有很多检测),我计算新的 (x,y) 和列表中每个点之间的距离。如果距离足够大,一定是第一个发现新的检测。然后我把新的 (x,y) 放到列表中。这很愚蠢,但确实有效。

目的是去除第一次检测到一个物体的(x,y)附近的点,只保留那个‘组’的一个点,然后迭代loc中的所有点来定位更多的‘组’,找到每组中只有一个点。

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】:

我列出了所有匹配项,对于每个新匹配项,我检查列表中的任何匹配项是否与边界框相交:

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)

这是检查交叉点的代码:

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】:

对于仍然想知道的人:首先对列表“zip(*loc[::-1)”进行排序要容易得多。

例如,我的脚本返回的结果如下:

(580, 822)
(871, 822)
(1017, 822)
(434, 823)
(726, 823)
(871, 823)
(1017, 823)
7

您会注意到有多个重复,但不只是按顺序重复。只需简单地使用“sorted(zip(*loc[::-1]))”对其进行排序,现在只需计算相邻的 2 点并检查每个循环的距离,就可以轻松获得距离。

通过在循环中添加条件并检查当前点的距离是否小于期望的距离,可以很好地完成工作。我从来没有正确地学习过python,所以我不确定这是否有效。至少这适用于我的用例。您可以在下面查看。

Source code (Github)/ Test Result (Imgur)


示例代码:

    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】:

我就是这样做的:

loc = np.where( res >= threshhold)
print(len(loc[0])) #match occurences count

loc 是二维数组。

【讨论】:

【参考方案6】:

进口时间 导入简历2 将 numpy 导入为 np 从 PIL 导入 ImageGrab

当真时:

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

【讨论】:

以上是关于如何计算模板匹配检测到的对象数量?的主要内容,如果未能解决你的问题,请参考以下文章

Python+OpenCV图像处理—— 模板匹配

Python+OpenCV图像处理—— 模板匹配

OpenCV图像处理:模版匹配和霍夫变换

旋转模板图像并执行模板匹配

opencv python:模板匹配

OpenCV之模板匹配