我试图围绕CV的基础知识。最初让我感兴趣的是模板匹配(在与CV无关的Pycon谈话中提到过),所以我想我会从那里开始。
我从这张图片开始:
我想要检测马里奥。所以我把他剪掉了:
我理解在图像周围滑动模板以查看最佳匹配的概念,并按照教程,我能够使用以下代码找到马里奥:
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中有一个掩码参数,但我还要进一步调查)
我的主要问题是模板匹配是否是检测大致相同但变化的图像的方式(例如他走路时),还是我应该研究另一种技术?
关于mmgp的建议,它应该可以用于匹配其他东西,我做了几个测试。
我用它作为匹配的模板:
然后拍了几张屏幕截图来测试匹配。
首先,我成功找到马里奥,并获得最大值1.
然而,试图找到跳跃马里奥导致完全失火。
现在已经批准了,模板中的马里奥,以及场景中的马里奥面向相反的方向,以及不同的动画帧,但我认为它们仍然匹配很多否则在图像中 - 如果仅用于颜色。但它将平台作为与模板最接近的匹配。
请注意,此值的最大值为0.728053808212
。
接下来,我尝试了一个没有马里奥的场景,看看会发生什么。
但奇怪的是,我将完全结果作为具有跳跃马里奥的图像 - 直到相似值:0.728053808212
。马里奥在照片中的效果与他不在画面中一样准确。
真奇怪!我不知道基础算法的实际细节,但我想从标准偏差的角度来看,场景中的框至少与模板中的红色匹配Mario的套装将是比蓝色平台更接近平均距离,对吧?所以,令人费解的是,它甚至不在我期望它的一般领域。
我猜这是我的用户错误,或者只是一个误解。
为什么有类似马里奥的场景与没有马里奥的场景有太多的匹配?
答案 0 :(得分:11)
没有方法是绝对可靠的,但模板匹配确实有很好的机会在那里工作。它可能需要一些预处理,直到有更大的样本(例如一个简短的视频)来演示可能的问题,尝试更高级的方法没有多大意义,因为有些库为你实现它们 - 尤其是如果你不知道他们应该在哪些条件下工作。
例如,以下是我使用模板匹配(红色矩形)得到的结果 - 所有这些都使用模板http://i.stack.imgur.com/EYs9B.png,甚至是最后一个:
为了实现这一点,我开始只考虑模板和输入图像的红色通道。由此我们可以轻松计算内部形态梯度,然后才进行匹配。为了在Mario不存在时不获取矩形,需要为匹配设置最小阈值。这是模板和这两个转换之后的图像之一:
以下是一些实现此目的的示例代码:
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匹配。
让我们假设你设法削减马里奥或获得马里奥的投资回报率图像。将此图像设为模板图像。现在,在主图像和模板中查找关键点。所以现在你有两组关键点。一个用于图像,另一个用于Mario(模板)。
[编辑]:
这是我使用SIFT和基于flann的knn匹配的方法。我还没有完成边界框部分。
由于您的模板非常小,SIFT和SURF不会提供很多关键点。但是为了获得大量的特征点,你可以尝试使用Harris Corner探测器。我在图像上应用了哈里斯的角落,我在马里奥身上得到了很好的分数。
如果您使用过SIFT或SURF,则会有图像和模板的描述符。使用KNN或其他一些有效的匹配算法匹配这些关键点。如果你正在使用OpenCV,我建议你研究基于flann的匹配器。匹配关键点后,您可能希望过滤掉不正确的匹配项。您可以通过K-最近邻居执行此操作,并且根据最近匹配的距离,您可以进一步过滤掉关键点。您可以使用前向后退错误进一步过滤匹配。
[编辑]: 如果您使用的是Harris Corner探测器,那么您只能得到分数而不是关键点。您可以将它们转换为关键点,也可以编写自己的蛮力数学运算符。这并不困难。
过滤关键点后,您的对象(在本例中为Mario)附近会有一组关键点,并且几乎没有分散的关键点。要消除这些分散的关键点,您可以使用群集。 DBSCAN群集将帮助您获得良好的积分群。
现在你有了一组关键点。使用k-means,您应该尝试找到群集的中心。获得群集的中心后,您可以估计边界框。
我希望这会有所帮助。
[编辑]
尝试使用 Harris Corners 来匹配积分。在过滤哈里斯角落之后,我使用强力方法来匹配这些点。一些更好的算法可能会给你更好的结果。