如何使用OpenCV和Python在图像中使用掩码(或透明度)查找模板?

30

假设我们正在寻找这个模板:

Stop

我们的模板角落是透明的,因此背景会变化,如下所示:

Stop on moon

Stop on everest

Stop on leaves

假设我们能够使用以下掩码与我们的模板:

Stop Stop mask

那么查找就非常容易。

我尝试的方法:

我尝试了 matchTemplate,但它不支持掩码(据我所知),使用模板中的 alpha 通道(透明度)也无法实现此功能,因为它会比较 alpha 通道而不是忽略这些像素。

我还研究了“感兴趣区域”,我认为这应该是解决方案,但是你只能指定一个矩形区域。我甚至不确定它是否适用于模板。

我相信通过编写自己的算法是可以实现的,但我希望能够利用标准的OpenCV来避免重复造轮子。更不用说,它很可能比我自己的算法更优化。

那么,我该如何使用OpenCV + Python来实现这样的功能呢?


2
如果您发布您尝试过的内容会更有帮助。不管怎样,这是一个非常有趣的问题。 - Morgan Wilde
谢谢您的评论。我已经添加了我尝试过的东西,虽然我认为这并不值得一提。 - user1973386
1
尝试使用SIFT/SURF匹配关键点。 - Froyo
我已经回答了一个类似的帖子: https://dev59.com/SZTfa4cB1Zd3GeqPRXyX#36047048 - Richard
在那里找到一个类似问题的答案: https://dev59.com/SZTfa4cB1Zd3GeqPRXyX#36047048 - Richard
显示剩余2条评论
6个回答

10
这可以仅使用matchTemplate函数来实现,但需要进行一些小的变通。
让我们分析默认指标(CV_TM_SQDIFF_NORMED)。根据matchTemplate文档,默认指标如下: R(x, y) = sum (I(x+x', y+y') - T(x', y'))^2 其中 I 是图像矩阵,T 是模板,R 是结果矩阵。求和是在模板坐标x'y'上进行的。
因此,让我们通过插入具有与T相同尺寸的权重矩阵W来改变这个指标。 Q(x, y) = sum W(x', y')*(I(x+x', y+y') - T(x', y'))^2 在这种情况下,通过设置W(x', y') = 0,您可以忽略像素。那么如何制作这样的指标呢?通过简单的数学:
Q(x, y) = sum W(x', y')*(I(x+x', y+y') - T(x', y'))^2
        = sum W(x', y')*(I(x+x', y+y')^2 - 2*I(x+x', y+y')*T(x', y') + T(x', y')^2)
        = sum {W(x', y')*I(x+x', y+y')^2} - sum{W(x', y')*2*I(x+x', y+y')*T(x', y')} + sum{W(x', y')*T(x', y')^2)}

因此,我们将 Q 指标分成三个单独的总和。 所有这些总和都可以使用 matchTemplate 函数进行计算(使用 CV_TM_CCORR 方法)。 即
sum {W(x', y')*I(x+x', y+y')^2} = matchTemplate(I^2, W, method=2)
sum{W(x', y')*2*I(x+x', y+y')*T(x', y')} = matchTemplate(I, 2*W*T, method=2)
sum{W(x', y')*T(x', y')^2)} = matchTemplate(T^2, W, method=2) = sum(W*T^2)

最后一个元素是常数,所以在最小化中它没有任何影响。另一方面,如果 Q 趋近于零,检查我们的模板是否完美匹配仍然可能有用。尽管如此,对于最后一个元素,我们实际上不需要使用 matchTemplate 函数,因为它可以直接计算。
最终伪代码如下:
result = matchTemplate(I^2, W, method=2) - matchTemplate(I, 2*W*T, method=2) + as.scalar(sum(W*T^2))

它是否真正按照定义执行?从数学上来说是的。实际上,由于matchTemplate函数是基于32位浮点数运算的,所以存在一些小的舍入误差,但我相信这不是一个大问题。
请注意,您可以扩展分析并为matchTemplate提供的任何指标设置加权等价物。
这对我实际上起作用了。很抱歉我没有提供实际代码。我正在使用R进行工作,因此我没有Python代码。但是,这个想法非常简单。
希望这可以帮助到你。

1
你的理论实际上是不可行的。为什么?因为matchTemplate()函数不像OpenCV文档所说的那样工作。它不是将模板在图像上滑动并逐个像素比较。如果OpenCV这样做,即使在C++代码中处理500x500像素的图像也需要60秒的时间。为了优化速度,OpenCV先计算图像和模板的DFT(傅里叶变换),然后在傅里叶空间中进行比较。这使得执行速度快了100倍,但也导致你的理论失败。 - Elmue
@Elmue 谢谢回复,但我不太同意。首先,CV_TM_CCORR - Vyga
1
@Elmue 谢谢回复,但我不太同意。是的,OpenCV 可以做出智能计算,可以通过四舍五入误差来去噪声。然而,使用 CV_TM_CCORR 函数,我们实际上可以计算出我列出的数值。到了一天结束时,智能计算得出的最终答案就如公式中所示。我实际上在 R 中进行了这个操作,并通过暴力函数检查了答案。 - Vyga
1
好的,如果你真的在实践中尝试了你的理论,我会相信你。但是对于数学文凭的人来说,你的公式很有趣,而普通程序员会感到非常孤立无援。OpenCV文档是一种耻辱,而你的公式又增加了一个头疼的问题。OpenCV是一个C++库,一些C++代码将非常有帮助。如何在C++中计算2wT或sum(..)?以及你所说的as.scalar(sum(W*T^2))是什么意思?'as'是什么?你应该用一些代码来解释一下! - Elmue

3

我曾经需要使用这种方法,即在“掩模”区域填充白噪声。然后在查找匹配项时,它就会被有效地从相关性中清除。否则,我会像你一样,在掩模区域得到虚假的匹配结果。


2
你的问题的一个答案是卷积。使用模板作为核并过滤图像。
目标Mat将在可能存在模板的密集亮区域。您需要对结果进行聚类(例如,均值漂移)。
这样,您将拥有广义霍夫变换或基于模板的卷积匹配的非常简单的实现。

2

1

1

2021更新:我一整天都在寻找模板透明度的解决方案,最终我想我找到了一个方法。matchTemplate()有一个mask参数,它似乎正好可以满足OP的需求:在搜索另一个图像中的模板时忽略某些像素。由于我的模板已经包含透明度,所以我决定将我的模板作为templatemask参数使用。令人惊讶的是,它起作用了。

我正在使用JavaScript和opencv4nodejs,因此以下Python代码片段可能完全不正确,但理论上是正确的,我相当肯定它应该会工作。

# Import OpenCV
import cv2 as cv

# Read both the image and the template
image = cv.imread("image.png", cv.IMREAD_COLOR)
template = cv.imread("template.png", cv.IMREAD_COLOR)

# Match with template as both template and mask parameter
result = cv.matchTemplate(image, template, cv.TM_CCORR_NORMED, None, template)

如果你感兴趣,这里有一个使用opencv4nodejs的JavaScript要点。

现在我想起来了,这似乎非常愚蠢和过于美好,但是我在大多数测试中都得到了良好的匹配(0.98+)。希望这能帮到你!


1
太棒了!谢谢。 - S P Sharan
我不太确定你的意思,难道“mask”不是指小图像的蒙版图像而不是模板吗? - Keith Mak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接