20

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

我从这张图片开始:

来自 SMB3 的场景

我想从中检测到马里奥。所以我把他删掉了:

水管工

我了解在图像周围滑动模板以查看最佳匹配的概念,并且按照教程,我可以使用以下代码找到 mario:

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

到目前为止一切顺利,但后来我意识到这是非常脆弱的。它只会找到具有特定背景并显示特定动画帧的马里奥。

所以我很好奇,鉴于马里奥将始终具有相同的马里奥属性,(大小,颜色)是否有一种技术可以让我找到他,无论他的当前框架是静止不动,还是各种跑步中的一种循环精灵?有点像模糊匹配,你可以在字符串上做,但对于图像。

也许既然他是唯一的红色东西,有一种方法可以简单地跟踪红色像素?

另一个问题是从模板中删除背景。也许这会帮助 MatchTemplate 函数找到马里奥,即使他与模板不完全匹配?截至目前,我不完全确定它是如何工作的(我看到 MatchTemplate 中有一个掩码参数,但我必须进一步调查)

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

更新:

尝试匹配其他马里奥


根据 mmgp 的建议,它应该可以匹配其他东西,我进行了一些测试。

我用这个作为模板来匹配:

超级马里奥

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

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

在此处输入图像描述

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

失火

现在,模板中的 mario 和场景中的 mario 面向相反的方向,并且是不同的动画帧,但我认为它们仍然比图像中的其他任何东西都匹配得多——如果只是为了单独的颜色。但它将平台定位为与模板最接近的匹配项。

请注意,这个最大值是0.728053808212.

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

在此处输入图像描述

但奇怪的是,我得到了作为跳跃马里奥图像的确切结果——直到相似度值:0.728053808212. 马里奥出现在照片中与他不在照片中一样准确。

真的很奇怪!我不知道底层算法的实际细节,但我想,从标准偏差的角度来看,场景中至少与模板马里奥西装中的红色相匹配的盒子会比蓝色更接近平均距离平台,对吧?因此,它甚至不在我期望的一般区域中,这更加令人困惑。

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

为什么一个有类似马里奥的场景会和没有马里奥的场景有同样多的匹配?

4

2 回答 2

13

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

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

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

为了实现这一点,我首先只考虑模板和输入图像的红色通道。从中我们很容易计算内部形态梯度,然后才执行匹配。为了在马里奥不存在时不得到矩形,需要为匹配设置一个最小阈值。这是这两个转换后的模板和图像之一:

在此处输入图像描述 在此处输入图像描述

这是一些示例代码来实现这一点:

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)

我希望它在更多不同的情况下会失败,但是有几个简单的调整要做。

于 2013-02-10T15:16:49.743 回答
12

模板匹配并不总是能给出好的结果。您应该查看关键点匹配。

Step1:找到关键点

让我们假设您设法剪掉了马里奥或获得了马里奥的 ROI 图像。将此图像设为您的模板图像。现在,在主图像和模板中找到关键点。所以现在你有两组关键点。一个用于图像,另一个用于马里奥(模板)。

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

[编辑]:

这就是我使用这种方法与 SIFT 和基于 flann 的 knn 匹配得到的结果。我还没有完成边界框部分。

在此处输入图像描述

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

在此处输入图像描述

Step2:匹配关键点

如果您使用过 SIFT 或 SURF,您将拥有图像和模板的描述符。使用 KNN 或其他一些有效的匹配算法匹配这些关键点。如果您使用的是 OpenCV,我建议您查看基于 flann 的匹配器。匹配关键点后,您需要过滤掉不正确的匹配项。您可以通过 K-最近邻来执行此操作,并且根据最近匹配的距离,您可以进一步过滤掉关键点。您可以使用 Forward-Backward Error 进一步过滤匹配项。

前后误差估计:

  1. 将模板关键点与图像关键点匹配这将为您提供一组匹配项。
  2. 将图像关键点与模板关键点匹配。这将为您提供另一组匹配项。
  3. 这两个集合的共同集合将过滤掉不正确的匹配。

在此处输入图像描述

[编辑]:如果您使用哈里斯角检测器,您将只得到点而不是关键点。您可以将它们转换为关键点或编写自己的蛮力数学器。这并不难。

第三步:估计

过滤关键点后,您的对象(在本例中为马里奥)附近会有一组关键点,并且有几个分散的关键点。要消除这些分散的关键点,您可以使用聚类。DBSCAN 聚类将帮助您获得良好的点聚类。

step4:边界框估计

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

我希望这有帮助。

[编辑]

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

在此处输入图像描述

于 2013-02-10T12:26:42.763 回答