У меня была та же проблема, что и у этого вопроса, начиная с 2J, но ни один из ответов не работал с Python 3, поэтому сегодня я потратил весь день на решение проблемы: python opencv cv2 matchTemplate с прозрачностью (графика взята из вопроса) Мне наконец удалось написать cv2.matchTemplate() с маской (прозрачность/альфа-канал) и python (3.9.1) и opencv-python (4.5.1.48).
Проблема заключалась в том, что я передавал маску как RGBA (RGB как нули), как в Python 2, и всегда находил (0,0) в качестве местоположения, потому что все значения результата всегда были равны нулю. Когда я затем попытался передать только альфа-канал, это сработало, как и ожидалось! Поскольку я ничего не нашел по теме с Python 3, я пишу новый вопрос, надеясь получить некоторые предложения по улучшению. В своем коде я написал две функции, одна из которых традиционно находит только одно местоположение (findImgLoc()
). Другой (findImgTresh()
) для получения всех координат, где значение выше порогового значения. Обе функции нормализуют (0 - 1) все методы и инвертируют результат SQDIFF и SQDIFF NORMED, поэтому вы получаете обратно только координаты, или во 2-м случае список координат, что на мой взгляд более дружелюбный к новичкам. Для тестирования у меня есть функция test()
для получения выходной графики и информации в выводе cmd. И только потому, что я снова и снова читаю это в старых ответах, с кодом маска работает со всеми методами сопоставления, а не только с TM_SQDIFF и TM_CCORR_NORMED!
Изображение
Шаблон
Результат без маски (findImgTresh()
с SQDIFF_NORMED
и thres=.95
-› 4 местоположения)
Вывод: [[15, 123], [15, 124], [15, 165], [15, 166]]
Результат с маской (findImgTresh()
с SQDIFF_NORMED
и thres=.95
-› 2 местоположения)
Вывод: [[15, 123], [15, 165]]
Результат без маски на изображении
Результат с маской на изображении
Код
import numpy as np
# Method: 0: SQDIFF | 1: SQDIFF NORMED | 2: TM CCORR | 3: TM CCORR NORMED | 4: TM COEFF | 5: TM COEFF NORMED
img_path = "/Path/to/your/file/source_image_name.png"
tem_path = "/Path/to/your/file/template_image_name.png"
def findImgLoc(image_path, template_path, mask=False, method=1):
img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_COLOR (cv2.IMREAD_UNCHANGED)
tem = cv2.imread(tem_path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_COLOR (cv2.IMREAD_UNCHANGED)
# Match template with and without mask
if mask and img.shape[2] == 4:
alpha_channel = np.array(cv2.split(tem)[3])
result = cv2.matchTemplate(img, tem, method, mask=alpha_channel)
else:
result = cv2.matchTemplate(img, tem, method)
# Nomrmalize result data to percent (0-1)
result = cv2.normalize(result, None, 0, 1, cv2.NORM_MINMAX, -1)
# Invert Image to work similar across all methods!
if method == 0 or method == 1: result = (1 - result)
_minVal, _maxVal, minLoc, maxLoc = cv2.minMaxLoc(result, None)
matchLoc = maxLoc
# Make the Result viewable
# view_result = cv2.normalize(result, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
cv2.rectangle(result, matchLoc, (matchLoc[0] + tem.shape[0], matchLoc[1] + tem.shape[1]), (255,0,0), 2)
return matchLoc
def findImgthres(image_path, template_path, mask=False, method=1, thres=.95):
img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_COLOR (cv2.IMREAD_UNCHANGED)
tem = cv2.imread(tem_path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_COLOR (cv2.IMREAD_UNCHANGED)
# Match template with and without mask
if mask and img.shape[2] == 4:
alpha_channel = np.array(cv2.split(tem)[3])
result = cv2.matchTemplate(img, tem, method, mask=alpha_channel)
else:
result = cv2.matchTemplate(img, tem, method)
# Nomrmalize result data to percent (0-1)
result = cv2.normalize(result, None, 0, 1, cv2.NORM_MINMAX, -1)
# Invert Image to work similar across all methods!
if method == 0 or method == 1: result = (1 - result)
result_list = np.argwhere(result > thres)
return result_list
def test():
global img_path, tem_path
mT = MatchTemplate()
def findImgthres(image_path, template_path, mask=False, method=1, thres=.95):
img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_COLOR (cv2.IMREAD_UNCHANGED)
tem = cv2.imread(tem_path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_COLOR (cv2.IMREAD_UNCHANGED)
# Match template with and without mask
if mask and img.shape[2] == 4:
alpha_channel = np.array(cv2.split(tem)[3])
result = cv2.matchTemplate(img, tem, method, mask=alpha_channel)
else:
result = cv2.matchTemplate(img, tem, method)
# Nomrmalize result data to percent (0-1)
result = cv2.normalize(result, None, 0, 1, cv2.NORM_MINMAX, -1)
# Invert Image to work similar across all methods!
if method == 0 or method == 1: result = (1 - result)
result_list = np.argwhere(result > thres)
return result, result_list
tmp_source = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
tmp_mask_source = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
tmp_tem = cv2.imread(tem_path, cv2.IMREAD_UNCHANGED)
result = findImgthres(img_path, tem_path)
mask_result = findImgthres(img_path, tem_path, mask=True)
# Mark original Image with mask
matchLoc = mask_result[1]
if matchLoc is not None:
for loc in matchLoc:
cv2.rectangle(tmp_mask_source, tuple(loc)[::-1], (loc[1] + tmp_tem.shape[1], loc[0] + tmp_tem.shape[0]), (0,0,0), 1)
# Mark original Image without mask
matchLoc = result[1]
if matchLoc is not None:
for loc in matchLoc:
cv2.rectangle(tmp_source, tuple(loc)[::-1], (loc[1] + tmp_tem.shape[1], loc[0] + tmp_tem.shape[0]), (0,0,0), 1)
# Save Images
_dir = os.path.dirname(__file__)
cv2.imwrite(os.path.join(_dir, "source.png") , tmp_source)
cv2.imwrite(os.path.join(_dir, "mask_source.png") , tmp_mask_source)
cv2.imwrite(os.path.join(_dir, "result.png") , 255*result[0])
cv2.imwrite(os.path.join(_dir, "mask_result.png") , 255*mask_result[0])
# Output Result
print(f"[NO MASK] Match Locs: {result[1]}")
print(f"[MASK] Match Locs: {mask_result[1]}")
if __name__ == "__main__":
test()
Я надеюсь, что смогу помочь в дальнейшем, и рад предложениям по улучшению!