gpt4 book ai didi

带 mask 的灰度图像上的 Python openCV matchTemplate

转载 作者:太空狗 更新时间:2023-10-30 01:05:25 30 4
gpt4 key购买 nike

我有一个项目,我想在图像中找到一堆箭头,如下所示:ibb.co/dSCAYQ使用以下模板:ibb.co/jpRUtQ

我在 Python 中使用 cv2 的模板匹配功能。我的算法是将模板旋转 360 度并匹配每次旋转。我得到以下结果:ibb.co/kDFB7k

如您所见,除了 2 个非常靠近的箭头(例如另一个箭头位于模板的黑色区域)外,它的效果很好。

我正在尝试使用掩码,但似乎 cv2 根本没有应用我的掩码,即无论掩码数组具有什么值,匹配都是相同的。已经尝试了两天,但 cv2 的有限文档没有帮助。

这是我的代码:

import numpy as np
import cv2
import os
from scipy import misc, ndimage

STRIPPED_DIR = #Image dir
TMPL_DIR = #Template dir
MATCH_THRESH = 0.9
MATCH_RES = 1 #specifies degree-interval at which to match

def make_templates():
base = misc.imread(os.path.join(TMPL_DIR,'base.jpg')) # The templ that I rotate to make 360 templates
for deg in range(360):
print('making template: ' + str(deg))
tmpl = ndimage.rotate(base, deg)
misc.imsave(os.path.join(TMPL_DIR, 'tmp' + str(deg) + '.jpg'), tmpl)

def make_masks():
for deg in range(360):
tmpl = cv2.imread(os.path.join(TMPL_DIR, 'tmp' + str(deg) + '.jpg'), 0)
ret2, mask = cv2.threshold(tmpl, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite(os.path.join(TMPL_DIR, 'mask' + str(deg) + '.jpg'), mask)

def match(img_name):
img_rgb = cv2.imread(os.path.join(STRIPPED_DIR, img_name))
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)

for deg in range(0, 360, MATCH_RES):
tmpl = cv2.imread(os.path.join(TMPL_DIR, 'tmp' + str(deg) + '.jpg'), 0)
mask = cv2.imread(os.path.join(TMPL_DIR, 'mask' + str(deg) + '.jpg'), 0)
w, h = tmpl.shape[::-1]
res = cv2.matchTemplate(img_gray, tmpl, cv2.TM_CCORR_NORMED, mask=mask)
loc = np.where( res >= MATCH_THRESH)

for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv2.imwrite('res.png',img_rgb)

有些事情我认为可能是错误的但不确定如何解决:

  1. mask/tmpl/img 应具有的 channel 数。我尝试了一个带有彩色 4 channel png 的示例 stackoverflow eg. ,但不确定它如何转换为灰度或 3 channel jpeg。
  2. 掩码数组的值。例如屏蔽掉的像素应该是 1 还是 255?

非常感谢任何帮助。

更新我修复了代码中的一个小错误; matchTemplate() 的参数中必须使用 mask=mask。这与使用 255 的掩码值相结合产生了差异。但是,现在我得到了大量的误报:<强> http://ibb.co/esfTnk 请注意,误报比真报的相关性更强。关于如何修复我的面具以解决此问题的任何指示?现在我只是简单地使用我的模板的黑白转换。

最佳答案

您已经弄清楚了第一个问题,但我将对它们进行一些扩展:

对于二进制掩码,它应该是 uint8 类型,其中值只是零或非零。零位置将被忽略,如果它们不为零,则包含在掩码中。您可以传递一个 float32 而不是作为掩码,在这种情况下,它可以让您加权像素;所以 0 的值是忽略,1 是包含,.5 是包含但只给它一半的权重作为另一个像素。请注意,只有 TM_SQDIFFTM_CCORR_NORMED 支持掩码,但这没关系,因为您使用的是后者。 matchTemplate 的掩码仅为单 channel 。正如您所发现的,mask 不是位置参数,因此必须使用参数中的键调用它,mask=your_mask。所有这些在 this page on the OpenCV docs 中都非常明确.

现在到新问题:

这与您使用的方法以及您使用 jpg 的事实有关。看看 formulas for the normed methods .在图像完全为零的地方,您将得到错误的结果,因为您将除以零。但这不是确切的问题——因为它返回 nan 并且 np.nan > value 总是返回 false,所以你永远不会从 中绘制正方形nan 值。

相反,问题出在边缘情况下,您会得到非零值的提示;并且因为您使用的是 jpg 图像,所以并非所有黑色值都恰好为 0;事实上,很多都不是。请注意公式中的平均值,当图像窗口中有 1、2、5 等值时,平均值将非常小,因此它会放大相关值。您应该改用 TM_SQDIFF(因为它是唯一允许掩码的其他方法)。此外,因为您使用的是 jpg,所以您的大部分掩码都毫无值(value),因为任何非零值(甚至 1)都算作包含。您应该使用 png 作为掩码。只要模板有合适的掩码,模板使用 jpg 还是 png 都没有关系。

使用 TM_SQDIFF,您不是在寻找最大值,而是在寻找最小值——您希望模板和图像补丁之间的差异最小。您知道差异应该非常小——像素完美匹配恰好为 0,您可能不会得到。您可以稍微调整一下阈值。请注意,您总是会为每次旋转获得非常接近的值,因为模板的性质——小箭头条几乎不会添加那么多正值,并且不一定保证一个度数的离散化是完全正确的(除非你那样制作图像)。但是,即使箭头指向完全错误的方向,也仍然会非常接近,因为有很多重叠;并且朝向正确方向的箭头将真正接近正确方向的值。

在运行代码时预览平方差的结果是什么:

res = cv2.matchTemplate(img_gray, tmpl, cv2.TM_SQDIFF, mask=mask)
cv2.imshow("result", res.astype(np.uint8))
if cv2.waitKey(0) & 0xFF == ord('q'):
break

Square difference image

你可以看到基本上模板的每个方向都非常匹配。

无论如何,它似乎达到了 8 的阈值:

Match locations with square differences and a threshold of 8

我在您的代码中唯一修改的是将所有图像更改为 png,切换到 TM_SQDIFF,确保 loc 查找值小于阈值而不是大于阈值,并使用 8 的 MATCH_THRESH。至少我认为这就是我所做的全部更改。看看以防万一:

import numpy as np
import cv2
import os
from scipy import misc, ndimage

STRIPPED_DIR = ...
TMPL_DIR = ...
MATCH_THRESH = 8
MATCH_RES = 1 #specifies degree-interval at which to match

def make_templates():
base = misc.imread(os.path.join(TMPL_DIR,'base.jpg')) # The templ that I rotate to make 360 templates
for deg in range(360):
print('making template: ' + str(deg))
tmpl = ndimage.rotate(base, deg)
misc.imsave(os.path.join(TMPL_DIR, 'tmp' + str(deg) + '.png'), tmpl)

def make_masks():
for deg in range(360):
tmpl = cv2.imread(os.path.join(TMPL_DIR, 'tmp' + str(deg) + '.png'), 0)
ret2, mask = cv2.threshold(tmpl, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite(os.path.join(TMPL_DIR, 'mask' + str(deg) + '.png'), mask)

def match(img_name):
img_rgb = cv2.imread(os.path.join(STRIPPED_DIR, img_name))
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)

for deg in range(0, 360, MATCH_RES):
tmpl = cv2.imread(os.path.join(TMPL_DIR, 'tmp' + str(deg) + '.png'), 0)
mask = cv2.imread(os.path.join(TMPL_DIR, 'mask' + str(deg) + '.png'), 0)
w, h = tmpl.shape[::-1]
res = cv2.matchTemplate(img_gray, tmpl, cv2.TM_SQDIFF, mask=mask)

loc = np.where(res < MATCH_THRESH)
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv2.imwrite('res.png',img_rgb)

关于带 mask 的灰度图像上的 Python openCV matchTemplate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44690002/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com