Сопоставление шаблонов с Python 3.9.1, opencv.python 4.5.1.48 и маской (прозрачность)

У меня была та же проблема, что и у этого вопроса, начиная с 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()

Я надеюсь, что смогу помочь в дальнейшем, и рад предложениям по улучшению!


person Philipp Reuter    schedule 28.01.2021    source источник