简体   繁体   English

如何模板匹配 OpenCV 中的简单二维形状?

[英]How to template match a simple 2D shape in OpenCV?

I want to detect all pawns on a simple 2D chessboard.我想检测一个简单的 2D 棋盘上的所有棋子。 The problem is, my code only detects pawns on dark squares.问题是,我的代码只检测深色方块上的棋子。 I need all white pawns detected.我需要检测到所有的白色棋子。 Here's my set up:这是我的设置:

Chessboard ( full_board.png ):棋盘( full_board.png ): 在此处输入图像描述

Pawn ( wp.png ):典当( wp.png ): 在此处输入图像描述

Expected output (detects all pawns):预期的 output (检测所有棋子): 在此处输入图像描述

Actual output (not all pawns are detected):实际 output (并非所有棋子都被检测到): 在此处输入图像描述

Code:代码:

import cv2
import numpy as np
import imutils

def main():
    img = cv2.imread('full_board.png', 0)   
    # Piece templates:
    img_rgb = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    img_gray = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2GRAY)

    pawn_white_template = cv2.imread('wp.png', 0)

    cv2.imshow("Template", pawn_white_template)
    cv2.waitKey(0)

    w_pawn_white, h_pawn_white = pawn_white_template.shape[::-1]

    res_pawn_white = cv2.matchTemplate(img_gray,pawn_white_template,cv2.TM_CCOEFF_NORMED)

    threshhold = 0.6
    loc = np.where(res_pawn_white >= threshhold)

    for pt in zip(*loc[::-1]):
        cv2.rectangle(img_rgb,pt,(pt[0]+w_pawn_white, pt[1]+h_pawn_white),(0,255,255),1)

    cv2.imshow('detected',img_rgb)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Changing threshold doesn't help me.改变阈值对我没有帮助。 I'm not sure why it is only detecting dark square pawns.我不确定为什么它只检测到深色方形棋子。 Any ideas how to detect all pawns?任何想法如何检测所有棋子?

If you remove the alpha channel, you will see that the background of the template is a dark green.如果你去掉 alpha 通道,你会看到模板的背景是深绿色的。 So it will only match the dark background squares.所以它只会匹配深色背景方块。 You are reading the template with alpha, but the alpha channel will not be used in the template matching.您正在阅读带有 alpha 的模板,但模板匹配中不会使用 alpha 通道。 You need to extract the alpha channel of the template as a mask and use the mask option in matchTemplate?您需要将模板的 alpha 通道提取为蒙版并使用 matchTemplate 中的蒙版选项吗? That should fix the issue.那应该可以解决问题。

You also seem to be converting the input to grayscale, but trying to match with a colored template.您似乎也将输入转换为灰度,但试图与彩色模板匹配。 Note that you can do template matching on colored images.请注意,您可以对彩色图像进行模板匹配。

Here is the template without alpha:这是没有 alpha 的模板:

在此处输入图像描述

Here is the alpha channel from the template:这是模板中的 Alpha 通道:

在此处输入图像描述

See https://docs.opencv.org/4.1.1/df/dfb/group__imgproc__object.html#ga586ebfb0a7fb604b35a23d85391329behttps://docs.opencv.org/4.1.1/df/dfb/group__imgproc__object.html#ga586ebfb0a7fb604b35a23d85391329be

mask -- Mask of searched template. mask -- 搜索模板的掩码。 It must have the same datatype and size with templ.它必须与 templ 具有相同的数据类型和大小。 It is not set by default.默认情况下未设置。 Currently, only the TM_SQDIFF and TM_CCORR_NORMED methods are supported.目前,仅支持 TM_SQDIFF 和 TM_CCORR_NORMED 方法。


In case of a color image, template summation in the numerator and each sum in the denominator is done over all of the channels and separate mean values are used for each channel.在彩色图像的情况下,分子中的模板总和和分母中的每个总和在所有通道上完成,并且每个通道使用单独的平均值。 That is, the function can take a color template and a color image.即function可以取彩色模板和彩色图像。 The result will still be a single-channel image, which is easier to analyze.结果仍然是单通道图像,更易于分析。

So here is your example in Python/OpenCV with color images and masked template matching.因此,这是您在 Python/OpenCV 中使用彩色图像和蒙版模板匹配的示例。

Input:输入:

在此处输入图像描述

Template:模板:

在此处输入图像描述

import cv2
import numpy as np

# read chessboard image
img = cv2.imread('chessboard.png')

# read pawn image template
template = cv2.imread('pawn.png', cv2.IMREAD_UNCHANGED)
hh, ww = template.shape[:2]

# extract pawn base image and alpha channel and make alpha 3 channels
pawn = template[:,:,0:3]
alpha = template[:,:,3]
alpha = cv2.merge([alpha,alpha,alpha])

# do masked template matching and save correlation image
correlation = cv2.matchTemplate(img, pawn, cv2.TM_CCORR_NORMED, mask=alpha)

# set threshold and get all matches
threshhold = 0.89
loc = np.where(correlation >= threshhold)

# draw matches 
result = img.copy()
for pt in zip(*loc[::-1]):
    cv2.rectangle(result, pt, (pt[0]+ww, pt[1]+hh), (0,0,255), 1)
    print(pt)

# save results
cv2.imwrite('chessboard_pawn.png', pawn)
cv2.imwrite('chessboard_alpha.png', alpha)
cv2.imwrite('chessboard_matches.jpg', result)  

cv2.imshow('pawn',pawn)
cv2.imshow('alpha',alpha)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Template without alpha channel:没有 Alpha 通道的模板:

在此处输入图像描述

Extracted alpha channel as mask:提取 alpha 通道作为掩码:

在此处输入图像描述

Resulting match locations on input:输入的结果匹配位置:

在此处输入图像描述

But note that each location is really several near-by matches.但请注意,每个位置实际上是几个附近的匹配项。 So one actually has too many matches.所以一个实际上有太多的比赛。

(83, 1052)
(252, 1052)
(253, 1052)
(254, 1052)
(423, 1052)
(592, 1052)
(593, 1052)
(594, 1052)
(763, 1052)
(932, 1052)
(933, 1052)
(934, 1052)
(1103, 1052)
(1272, 1052)
(1273, 1052)
(1274, 1052)
(82, 1053)
(83, 1053)
(84, 1053)
(252, 1053)
(253, 1053)
(254, 1053)
(422, 1053)
(423, 1053)
(424, 1053)
(592, 1053)
(593, 1053)
(594, 1053)
(762, 1053)
(763, 1053)
(764, 1053)
(932, 1053)
(933, 1053)
(934, 1053)
(1102, 1053)
(1103, 1053)
(1104, 1053)
(1272, 1053)
(1273, 1053)
(1274, 1053)
(82, 1054)
(83, 1054)
(84, 1054)
(252, 1054)
(253, 1054)
(254, 1054)
(422, 1054)
(423, 1054)
(424, 1054)
(592, 1054)
(593, 1054)
(594, 1054)
(762, 1054)
(763, 1054)
(764, 1054)
(932, 1054)
(933, 1054)
(934, 1054)
(1102, 1054)
(1103, 1054)
(1104, 1054)
(1272, 1054)
(1273, 1054)
(1274, 1054)
(82, 1055)
(83, 1055)
(84, 1055)
(252, 1055)
(253, 1055)
(254, 1055)
(422, 1055)
(423, 1055)
(424, 1055)
(592, 1055)
(593, 1055)
(594, 1055)
(762, 1055)
(763, 1055)
(764, 1055)
(932, 1055)
(933, 1055)
(934, 1055)
(1102, 1055)
(1103, 1055)
(1104, 1055)
(1272, 1055)
(1273, 1055)
(1274, 1055)

The proper way to deal with multiple matches would be to mask out each match region in the correlation image in a loop, so that nearby non-peak matches that are above the threshold are avoided.处理多个匹配的正确方法是在循环中屏蔽相关图像中的每个匹配区域,从而避免附近高于阈值的非峰值匹配。

Here is one way to do that.这是一种方法。

import cv2
import numpy as np
import math

# read chessboard image
img = cv2.imread('chessboard.png')

# read pawn image template
template = cv2.imread('pawn.png', cv2.IMREAD_UNCHANGED)
hh, ww = template.shape[:2]

# extract pawn base image and alpha channel and make alpha 3 channels
pawn = template[:,:,0:3]
alpha = template[:,:,3]
alpha = cv2.merge([alpha,alpha,alpha])


# set threshold
threshold = 0.89

# do masked template matching and save correlation image
corr_img = cv2.matchTemplate(img, pawn, cv2.TM_CCORR_NORMED, mask=alpha)

# search for max score
result = img.copy()
max_val = 1
rad = int(math.sqrt(hh*hh+ww*ww)/4)
while max_val > threshold:

    # find max value of correlation image
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(corr_img)
    print(max_val, max_loc)

    if max_val > threshold:
        # draw match on copy of input
        cv2.rectangle(result, max_loc, (max_loc[0]+ww, max_loc[1]+hh), (0,0,255), 2)

        # write black circle at max_loc in corr_img
        cv2.circle(corr_img, (max_loc), radius=rad, color=0, thickness=cv2.FILLED)

    else:
        break

# save results
cv2.imwrite('chessboard_pawn.png', pawn)
cv2.imwrite('chessboard_alpha.png', alpha)
cv2.imwrite('chessboard_correlation.png', (255*corr_img).clip(0,255).astype(np.uint8))
cv2.imwrite('chessboard_matches2.jpg', result)

cv2.imshow('pawn',pawn)
cv2.imshow('alpha',alpha)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Resulting Matches:结果匹配:

在此处输入图像描述

And here are the actual matches with their scores:以下是与他们的分数的实际匹配:

0.8956151008605957 (253, 1053)
0.8956151008605957 (593, 1053)
0.8956151008605957 (933, 1053)
0.8956151008605957 (1273, 1053)
0.89393150806427 (83, 1054)
0.89393150806427 (423, 1054)
0.89393150806427 (763, 1054)
0.89393150806427 (1103, 1054)
0.886812150478363 (1128, 1232)


Correlation Image with circlular masked out regions:带有圆形遮蔽区域的相关图像:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM