OpenCV 模板匹配和透明度

40

OpenCV在模板匹配中如何处理图像的透明度?

问题在于模板图像需要具有透明部分,因为在原始图像中这些位置可能是其他内容。

我尝试了所有方法,但它们都没有产生积极的结果(例如,在原始图像中检测到的模板位置不正确)。


设置最大值的阈值。如果低于阈值,则图像不包含模板。您需要通过实验确定阈值的度量。 - mpenkov
5
找不到图片。 =( - anatoly techtonik
@anatolytechtonik 抱歉,我已经没有它们了(已经过去5年了)。 - mnn
8个回答

23

看起来OpenCV似乎没有按照您想要的方式处理alpha通道。

你有两个选择:

  1. 编写自己的交叉相关方法,该方法将使用alpha通道
  2. 转换您的图像,使您的alpha通道变得无关紧要

由于第一种选择很简单,因此我将在此探讨第二种选择。我将重复使用我之前提供给类似问题的示例代码。如果您直接对图像应用交叉相关,则背景会干扰模板匹配(特别是浅色背景部分)。如果您尝试玩弄颜色通道,则会发现在蓝色通道中匹配会给出正确的结果。这取决于图像内容,并不是解决问题的一致方法。

另一个选择是对图像和模板执行边缘检测(例如Sobel),然后执行交叉相关。以下是边缘检测图像(我在GIMP中对Luma通道使用了Sobel边缘检测器,然后进行了一些强度拉伸)。

map

building

正如您所看到的,这里的 alpha 通道已经变得不相关了,因为大多数地形已经成为零强度,并且不会对交叉相关计算产生影响。因此,现在可以直接应用交叉相关,得到所需的结果:

misha@misha-desktop:~/Desktop/stackoverflow$ python cross-correlation.py map-blue.png building-maskz-blue.png 
(163, 244)

最后,这里有另一个相关的问题

附注:这是什么游戏?


谢谢,但是我使用灰度图像和模板在Sobel上没有得到相同的结果(请参见问题)。该图像来自一个旧的DOS游戏 - Earth 2140。 - mnn
你的图片无法工作的原因是因为没有边缘的区域不是黑色(它们是中性灰色的127)。与我的图像进行比较。你需要使非边缘区域变成,以便它们不会干扰交叉相关计算。 - mpenkov
好的,我使用cvConvertAbsScale将非边缘区域设为零。(见问题) 但是,我的Sobel仍然与你的不同(特别是模板)。这可能是因为我使用了OpenCV,而你用GIMP进行Sobel操作吗? - mnn
边缘检测可能在某些特定情况下是一个解决方法。但如果模板是一个滑动颜色的彩虹呢?没有边缘可言。你的技巧有时可能管用,但这只是一个丑陋的解决方案,因为OpenCV中缺少了一个真正的掩码模板匹配函数。 - Elmue
我认为这是帝国时代 @mpenkov - Dan
显示剩余2条评论

17

OpenCV 3.0提供原生支持带有掩膜模板的模板匹配。请参考新文档

参数:

image ...

templ ...

result ...

method ...

mask 模板掩膜。它必须与templ具有相同的数据类型和尺寸。默认情况下未设置。

[稍微偏离话题]

需要注意的是,不可能对带有掩膜的参考图像(较大的图像)进行模板匹配。这是有道理的,因为OpenCV使用基于FFT的模板匹配。

因此,如果您仅需要在参考图像的特定区域执行模板匹配,则需要自己实现该方法或遮罩cv :: matchTemplate的输出。

从头开始实现应该能够弥补只想在非常特定的区域(即:在哈里斯角附近)搜索模板的情况。


15
我有一个相对简单的解决方案,似乎在实际中能够达到较好的效果:用噪点替换模板图片的透明通道,在匹配过程中透明区域就变得可以忽略了。

比如说,我的应用场景是从iOS的屏幕截图中寻找表情符号。iOS键盘背景会根据上下文变换颜色,如果你在模板图片中使用了特定的背景颜色,匹配过程就会出现问题。

这里是alpha通道的原始模板图片:
raw template image on alpha

这里是经处理后,噪点填补了alpha通道的模板图片:
enter image description here

我将经过处理的模板图片输入OpenCV文档提供的模板匹配样例代码进行匹配。无论是在黑暗背景还是浅色背景下,匹配都能够以相当高的准确率进行。

在黑色背景下搜索:

matched on dark

在浅色背景上搜索:

matched on light

相比之下,将模板的alpha通道保持透明,或者承诺使用深色或浅色背景,都不能返回可接受的匹配结果。

6
你的解决方案是一个功能不良的变通方法。正常情况下,如果图像完全相同,matchTemplate()函数返回99%或甚至100%匹配度的保证,而你提供的样本图像对应的匹配保证只有23%。下一个不匹配的图像(其中包括一个表情符号)的匹配程度仅为11%。这种匹配(23%)和非匹配(11%)之间的距离非常糟糕。而且,表情符号与你的模板图像是完全不同的事物。因此,这种解决方案强烈依赖于你用来区分匹配和非匹配的阈值。你的解决方案的结果很差。 - Elmue
2
因此,更好的解决方法是:首先使用您的方法(噪声模板)通过matchTemplate()找到可能的匹配位置,然后在第二步中,在第一步中找到的位置上将透明部分遮盖为黑色,以获取真实的确定性(高达100%),包括模板和主图像。 - Elmue
1
如果您删除表情周围的无用噪音,您将获得更好的确定性结果。您有16个像素以上和以下以及每侧5个像素的噪音。删除它们后,确定性从23%增加到57%。噪音越多,识别效果越差! - Elmue

1

SQDIFF/SQDIFF_N选项是一种解决方案,如果您尝试用黑色RGB颜色替换alpha通道。 至少这是我解决同样问题的方法。从我的结果来看,这种方法对较亮的像素值比较敏感,我抓住了这个机会。


1
OpenCV将透明度视为图像的一部分,而不是忽略它,这可能会导致意外结果。我处理它的方式是使用一个带有透明度的模板,并在matchTemplate()中使用mask参数。我在这里回答了一个类似的问题,并提供了更详细的解释,也许可以帮到你。

-1
我认为你正在尝试使用掩码进行OpenCV中的模板匹配。我认为你可以尝试在模板上设置ROI(感兴趣区域)。这个SO问题显示如何做到这一点。(请注意,在该问题中,ROI是设置在目标图像上而不是模板上,但过程是相同的。)

有趣,但并没有真正帮助,因为我无法将搜索范围缩小到这样的区域(模板图像可能位于原始图像的任何位置)。 - mnn
没错。但是在模板本身中有一些透明的像素(即不应该在模板ROI中),还有一些不透明的像素(即应该在模板ROI中)。最糟糕的情况是(如@Utkarsh Shinha所说),您必须编写自己的模板匹配函数以忽略不在ROI中的像素。 - carlosdc
看一下示例图像。透明区域不是矩形,而ROI是一个矩形。 - mnn
carlosdc,你没有理解问题。 - Elmue

-1

我不确定,但透明通道的处理方式与其他通道相同。如果模板中的像素是“透明”的,则在主图像上也应该是“透明”的。这只是我的猜测。


这就是问题所在。我需要模板匹配来“忽略”模板图像中透明的像素。否则,我将永远无法在原始图像中找到模板,因为在原始图像中,我要查找的对象周围可能有任何东西。 - mnn
去掉两者的透明通道。这可能有效。或者你可以编写自己的模板匹配函数。OpenCV文档列出了各种方法使用的公式。您可以修改它们,使它们“尊重”像素处的透明度量。 - Utkarsh Sinha
Utkarash,你是对的:你只是在猜测。但现实世界比你想象的要复杂得多。当你编写自己的匹配函数,使用给定的公式逐像素比较图像和模板时,即使在速度优化的C++中运行,这将是不可接受的缓慢(最多需要一分钟)。OpenCV之所以如此快,是因为它在matchTemplate()中使用了DFT(傅里叶变换)。但代码非常复杂(并且没有任何注释),只有数学家才能理解它。所以忘记编写自己的matchTemplate()函数吧! - Elmue

-2

我遇到了同样的问题,然后想出了一个解决方案。假设referenceImageMask和templateMask在好像素中有1,在坏像素中有0。而且referenceImage和templateImage已经被掩蔽,并且在坏像素中也有0。

然后,模板匹配的第一个结果将给出图像之间未归一化的交叉相关性。但是,许多像素为零。

第二个模板匹配将为每个可能的偏移量提供在两个图像中同时不为零(未掩蔽)的像素数量。

然后,通过该数字对相关性进行归一化应该会得到您(和我)想要的值。在两个图像中未被掩蔽的像素的平均乘积。

Image<Gray, float> imCorr = referenceImage.MatchTemplate(templateImage,      Emgu.CV.CvEnum.TM_TYPE.CV_TM_CCORR);
Image<Gray, float> imCorrMask = referenceImageMask.MatchTemplate(templateMask, Emgu.CV.CvEnum.TM_TYPE.CV_TM_CCORR);
_imCorr = _imCorr.Mul(_imCorrMask.Pow(-1));

更新:实际上,这个解决方案并不可行。因为opencv中的交叉相关实现使用了DFT,所以会出现数值问题,你不能使用第二个交叉相关来纠正第一个。


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