模糊模板匹配?

时间:2013-02-09 22:03:15

标签: opencv computer-vision

我试图围绕CV的基础知识。最初让我感兴趣的是模板匹配(在与CV无关的Pycon谈话中提到过),所以我想我会从那里开始。

我从这张图片开始:

Scene from SMB3

我想要检测马里奥。所以我把他剪掉了:

The Plumber

我理解在图像周围滑动模板以查看最佳匹配的概念,并按照教程,我能够使用以下代码找到马里奥:

def match_template(img, template):
    s = time.time()
    img_size = cv.GetSize(img)
    template_size = cv.GetSize(template)

    img_result = cv.CreateImage((img_size[0] - template_size[0] + 1, 
                            img_size[1] - template_size[1] + 1), cv.IPL_DEPTH_32F, 1)
    cv.Zero(img_result)

    cv.MatchTemplate(img, template, img_result, cv.CV_TM_CCORR_NORMED)
    min_val, max_val, min_loc, max_loc = cv.MinMaxLoc(img_result)
    # inspect.getargspec(cv.MinMaxLoc)
    print min_val
    print max_val 
    print min_loc 
    print max_loc
    cv.Rectangle(img, max_loc, (max_loc[0] + template.width, max_loc[1] + template.height), cv.Scalar(120.), 2)
    print time.time() - s
    cv.NamedWindow("Result")
    cv.ShowImage("Result", img)
    cv.WaitKey(0)
    cv.DestroyAllWindows()

到目前为止一直很好,但后来我意识到这是非常脆弱的。它只会找到具有该特定背景的Mario,并且会显示特定的动画帧。

所以我很好奇,因为马里奥总是拥有相同的马里奥属性,(大小,颜色)是否有一种技术可以让我找到他,无论他的当前框架是否静止不动,或者各种运行循环精灵之一?有点像模糊匹配,你可以对字符串,但对图像。

也许因为他是唯一的红色东西,有一种简单跟踪红色像素的方法吗?

另一个问题是从模板中删除背景。也许这会帮助MatchTemplate函数找到Mario,尽管他并不完全符合模板?截至目前,我并不完全确定它是如何工作的(我看到MatchTemplate中有一个掩码参数,但我还要进一步调查)

我的主要问题是模板匹配是否是检测大致相同但变化的图像的方式(例如他走路时),还是我应该研究另一种技术?

更新

尝试匹配其他Marios


关于mmgp的建议,它应该可以用于匹配其他东西,我做了几个测试。

我用它作为匹配的模板:

Super mario

然后拍了几张屏幕截图来测试匹配。

首先,我成功找到马里奥,并获得最大值1.

enter image description here

然而,试图找到跳跃马里奥导致完全失火。

Misfire

现在已经批准了,模板中的马里奥,以及场景中的马里奥面向相反的方向,以及不同的动画帧,但我认为它们仍然匹配很多否则在图像中 - 如果仅用于颜色。但它将平台作为与模板最接近的匹配。

请注意,此值的最大值为0.728053808212

接下来,我尝试了一个没有马里奥的场景,看看会发生什么。

enter image description here

但奇怪的是,我将完全结果作为具有跳跃马里奥的图像 - 直到相似值:0.728053808212。马里奥在照片中的效果与他在画面中一样准确。

真奇怪!我不知道基础算法的实际细节,但我想从标准偏差的角度来看,场景中的框至少与模板中的红色匹配Mario的套装将是比蓝色平台更接近平均距离,对吧?所以,令人费解的是,它甚至不在我期望它的一般领域。

我猜这是我的用户错误,或者只是一个误解。

为什么有类似马里奥的场景与没有马里奥的场景有太多的匹配?

2 个答案:

答案 0 :(得分:11)

没有方法是绝对可靠的,但模板匹配确实有很好的机会在那里工作。它可能需要一些预处理,直到有更大的样本(例如一个简短的视频)来演示可能的问题,尝试更高级的方法没有多大意义,因为有些库为你实现它们 - 尤其是如果你不知道他们应该在哪些条件下工作。

例如,以下是我使用模板匹配(红色矩形)得到的结果 - 所有这些都使用模板http://i.stack.imgur.com/EYs9B.png,甚至是最后一个:

enter image description here enter image description here enter image description here enter image description here

为了实现这一点,我开始只考虑模板和输入图像的红色通道。由此我们可以轻松计算内部形态梯度,然后才进行匹配。为了在Mario不存在时不获取矩形,需要为匹配设置最小阈值。这是模板和这两个转换之后的图像之一:

enter image description here enter image description here

以下是一些实现此目的的示例代码:

import sys
import cv2
import numpy

img = cv2.imread(sys.argv[1])

img2 = img[:,:,2]
img2 = img2 - cv2.erode(img2, None)
template = cv2.imread(sys.argv[2])[:,:,2]
template = template - cv2.erode(template, None)

ccnorm = cv2.matchTemplate(img2, template, cv2.TM_CCORR_NORMED)
print ccnorm.max()
loc = numpy.where(ccnorm == ccnorm.max())
threshold = 0.4
th, tw = template.shape[:2]
for pt in zip(*loc[::-1]):
    if ccnorm[pt[::-1]] < threshold:
        continue
    cv2.rectangle(img, pt, (pt[0] + tw, pt[1] + th),
            (0, 0, 255), 2)

cv2.imwrite(sys.argv[2], img)

我希望它能在更多种情况下失败,但还有一些简单的调整要做。

答案 1 :(得分:10)

模板匹配并不总能产生良好的效果。你应该研究Keypoints匹配。

第1步:查找关键点

让我们假设你设法削减马里奥或获得马里奥的投资回报率图像。将此图像设为模板图像。现在,在主图像和模板中查找关键点。所以现在你有两组关键点。一个用于图像,另一个用于Mario(模板)。

您可以根据自己的喜好使用SIFTSURFORB

[编辑]:

这是我使用SIFT和基于flann的knn匹配的方法。我还没有完成边界框部分。

enter image description here

由于您的模板非常小,SIFT和SURF不会提供很多关键点。但是为了获得大量的特征点,你可以尝试使用Harris Corner探测器。我在图像上应用了哈里斯的角落,我在马里奥身上得到了很好的分数。

enter image description here

第2步:匹配关键点

如果您使用过SIFT或SURF,则会有图像和模板的描述符。使用KNN或其他一些有效的匹配算法匹配这些关键点。如果你正在使用OpenCV,我建议你研究基于flann的匹配器。匹配关键点后,您可能希望过滤掉不正确的匹配项。您可以通过K-最近邻居执行此操作,并且根据最近匹配的距离,您可以进一步过滤掉关键点。您可以使用前向后退错误进一步过滤匹配。

前向后向误差估计:

  1. 将模板关键点与图像关键点匹配这将为您提供一组匹配。
  2. 将图像关键点与模板关键点匹配。这将为您提供另一组匹配。
  3. 这两个集合的通用集将过滤掉不正确的匹配。
  4. enter image description here

    [编辑]: 如果您使用的是Harris Corner探测器,那么您只能得到分数而不是关键点。您可以将它们转换为关键点,也可以编写自己的蛮力数学运算符。这并不困难。

    第3步:估算

    过滤关键点后,您的对象(在本例中为Mario)附近会有一组关键点,并且几乎没有分散的关键点。要消除这些分散的关键点,您可以使用群集。 DBSCAN群集将帮助您获得良好的积分群。

    步骤4:边界框估计

    现在你有了一组关键点。使用k-means,您应该尝试找到群集的中心。获得群集的中心后,您可以估计边界框。

    我希望这会有所帮助。

    [编辑]

    尝试使用 Harris Corners 来匹配积分。在过滤哈里斯角落之后,我使用强力方法来匹配这些点。一些更好的算法可能会给你更好的结果。

    enter image description here