【问题标题】:Reducing misses on template matching (using transparent template)减少模板匹配的失误(使用透明模板)
【发布时间】:2020-08-06 21:29:12
【问题描述】:

我正在尝试确定拼图图像上拼图的位置。 我有

  1. 拼图图片(透明 png)
  2. 拼图板的图像,正确位置周围有白色轮廓

首先我提取图像 1 上的轮廓。并用它来绘制我的最终模板。 然后我将最终模板与拼图板匹配并保存一些结果图像。

以下是我得到的结果的一些示例 https://imgur.com/a/ZYyw7tU

在某些图像上,显然存在很多不匹配,对于某些图像,增加阈值将隐藏正确匹配,而某些不匹配将保留。

任何关于优化的提示或想法将不胜感激!

这是我的完整代码:

full_image = cv2.imread('puzzle_1.jpg')    
piece = cv2.imread('piece_1.png', cv2.IMREAD_UNCHANGED)
partial_image = cv2.cvtColor(piece,cv2.COLOR_BGR2GRAY)

contours, hierarchy = cv2.findContours(partial_image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
   
template = np.zeros((55, 55, 4), dtype=np.uint8)

cv2.drawContours(template, contours, -1, (255, 255, 255, 255),1)
hh, ww = template.shape[:2]

puzzleP = template[:,:,0:3]
alpha = template[:,:,3]
alpha = cv2.merge([alpha,alpha,alpha])

correlation = cv2.matchTemplate(full_image, puzzleP, cv2.TM_CCORR_NORMED, mask=alpha)

threshhold = 0.98
loc = np.where(correlation >= threshhold)

result = full_image.copy()
for pt in zip(*loc[::-1]):
    cv2.rectangle(result, pt, (pt[0]+ww, pt[1]+hh), (0,0,255), 1)
    print(pt)
    
cv2.imwrite('puzzle_piece.png', puzzleP)
cv2.imwrite('full_image_alpha.png', alpha)
cv2.imwrite('full_image_matches.jpg', result)

编辑: 这是两个文件(piece_1.png和puzzle_1.jpg)的示例(此示例有很多不匹配的地方) https://imgur.com/a/nGSXcNg

【问题讨论】:

  • 请发布您的输入和模板图片
  • 添加了puzzle_1.jpg和piece_1.png的链接
  • 你为什么把每场比赛都画在你的阈值之上?没有意义。它不排除也高于您的阈值的小变化。除非您希望在多个地方找到模板,否则只需为每个输入图像绘制最佳匹配。如果由于拼图多次出现而预期多个匹配,那么您需要多次循环输入,每次都将前一个最佳匹配周围的区域涂黑,以便轻微的变化不会仍然匹配高于您的阈值,以便您将单个匹配项与非常接近的匹配项分开。
  • 您还需要使模板的内部也透明。这样您就可以匹配拼图框架形状,我假设它出现在模板中。如果不这样做,那么由于亮度变化,模板内的纹理与图像中正确区域内的纹理不匹配,因此您将得到错误的匹配。另一种方法是将您的图像和模板转换为边缘并匹配它们。或者通过减去平均值并除以标准偏差来标准化您的图像和模板。但是,我认为后者不能很好地正常化。
  • 阈值代码直接来自您为回答我之前的问题而编写的链接:stackoverflow.com/questions/61779288/…。我只会考虑绘制最佳匹配,但实际上最佳匹配通常不是最佳匹配,这让我感到困惑。匹配是在绘制矩形之前完成的,所以我看不出它会如何影响匹配?

标签: python opencv computer-vision


【解决方案1】:

这似乎对我在 Python/OpenCV 中的给定图像上工作得很好。

  • 读取大小图像
  • 小图转灰度,阈值转二值
  • 获取二值图像的最大轮廓
  • 在黑色背景上将该轮廓绘制为白色以用作蒙版
  • 提取透明小图的BGR通道作为模板
  • 做模板匹配,得到最大匹配位置
  • 在大图的副本上绘制匹配框
  • 保存结果

大图:

小图:

import cv2
import numpy as np

# read images
full_image = cv2.imread('puzzle_1.jpg')    
piece = cv2.imread('piece_1.png', cv2.IMREAD_UNCHANGED)

# convert piece to gray and threshold to binary
partial_image = cv2.cvtColor(piece,cv2.COLOR_BGR2GRAY)
partial_image = cv2.threshold(partial_image, 0, 255, cv2.THRESH_BINARY)[1]

# get largest contour from binary image
contours = cv2.findContours(partial_image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# draw the contour of the piece outline as the mask
mask = np.zeros((55, 55, 3), dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, (255,255,255), 1)
hh, ww = mask.shape[:2]

# extract the template from the BGR (no alpha) piece 
template = piece[:,:,0:3]

correlation = cv2.matchTemplate(full_image, template, cv2.TM_CCORR_NORMED, mask=mask)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(correlation)
max_val_ncc = '{:.3f}'.format(max_val)
print("normalize_cross_correlation: " + max_val_ncc)
xx = max_loc[0]
yy = max_loc[1]

print('xmatchloc =',xx,'ymatchloc =',yy)

# draw template bounds and corner intersection in red onto img
result = full_image.copy()
cv2.rectangle(result, (xx, yy), (xx+ww, yy+hh), (0, 0, 255), 1)

# save results
cv2.imwrite('puzzle_template.png', template)
cv2.imwrite('puzzle_mask.png', mask)
cv2.imwrite('full_image_matches.jpg', result)

# show results
cv2.imshow('full_image', full_image)
cv2.imshow('piece', piece)
cv2.imshow('partial_image', partial_image)
cv2.imshow('mask', mask)
cv2.imshow('template', template)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

(bgr) 模板:

面具:

最大匹配位置:

【讨论】:

  • 我建议您查看中间图像,看看哪里出错了。我的直觉是您为蒙版(alpha)创建的轮廓图像不正确。我还注意到,此模板和蒙版的组合将与您的白色天空紧密匹配,除了天空比您的作品的白色边框稍暗。
  • 非常感谢,你是个传奇。这很好用。从这个迷你项目中学到了很多东西!哈哈
  • 如果我想调整拼图模板的大小怎么办。我试过做 piece = cv2.imread('piece_1.png', cv2.IMREAD_UNCHANGED) piece = cv2.resize(piece, (56, 55)) 并改变掩码 mask = np.zeros((56, 55 , 3), dtype=np.uint8) 但是我得到一个大小不匹配错误(-209:输入参数的大小不匹配)
猜你喜欢
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 1970-01-01
相关资源
最近更新 更多