Search code examples
pythonopencvcomputer-visiontemplate-matching

Multi-Scale template Matching doesn't work right


I have done multi-scale template matching in real-time from looking at this article. When the template appears in the frame, it detects it and drawing a bounding box around it which means it works fine. But when there is no template in the frame also, it detects somewhere and drawing the bounding box. I'll mention the code and the error that I recognized.

import cv2 as cv2
import numpy as np
import imutils


def main():

    template1 = cv2.imread("C:\\Users\\Manthika\\Desktop\\opencvtest\\template.jpg")
    template1 = cv2.cvtColor(template1, cv2.COLOR_BGR2GRAY)
    template1 = cv2.Canny(template1, 50, 200)
    template = imutils.resize(template1, width=60)
    (tH, tW) = template.shape[:2]
    cv2.imshow("Template", template)

    windowName = "Something"
    cv2.namedWindow(windowName)
    cap = cv2.VideoCapture(0)

    if cap.isOpened():
        ret, frame = cap.read()
    else:
        ret = False

    # loop over the frames to find the template
    while ret:
        # load the image, convert it to grayscale, and initialize the
        # bookkeeping variable to keep track of the matched region
        ret, frame = cap.read()
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        found = None

        # loop over the scales of the image
        for scale in np.linspace(0.2, 1.0, 20)[::-1]:
            # resize the image according to the scale, and keep track
            # of the ratio of the resizing
            resized = imutils.resize(gray, width=int(gray.shape[1] * scale))
            r = gray.shape[1] / float(resized.shape[1])

            # if the resized image is smaller than the template, then break
            # from the loop
            if resized.shape[0] < tH or resized.shape[1] < tW:
                print("frame is smaller than the template")
                break

            # detect edges in the resized, grayscale image and apply template
            # matching to find the template in the image
            edged = cv2.Canny(resized, 50, 200)
            result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF)
            (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)

            # if we have found a new maximum correlation value, then update
            # the bookkeeping variable
            if found is None or maxVal > found[0]:
                found = (maxVal, maxLoc, r)

            # unpack the bookkeeping variable and compute the (x, y) coordinates
            # of the bounding box based on the resized ratio
        # print(found)
        if found is None:
            # just show only the frames if the template is not detected
            cv2.imshow(windowName, frame)
            print("No template is found")
        else:
            (_, maxLoc, r) = found
            (startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
            (endX, endY) = (int((maxLoc[0] + tW) * r), int((maxLoc[1] + tH) * r))
            print(startX, startY, endX, endY)

            # draw a bounding box around the detected result and display the image
            cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 0, 255), 2)
            cv2.imshow(windowName, frame)

        if cv2.waitKey(1) == 27:
            break

    cv2.destroyAllWindows()
    cap.release()


if __name__ == "__main__":
    main() 

I think the problem is in this two lines,

if found is None or maxVal > found[0]:
                found = (maxVal, maxLoc, r)

found variable always updates with a value even if it is none. I'm new to computer vision so please be kind and help me to solve this problem. And also kindly let me know if I need to mention anything else. Thank you.


Solution

  • Refer to How do I use OpenCV MatchTemplate?:

    In your code, you have (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result), where it should be minVal,maxVal,minLoc,maxLoc = cv.MinMaxLoc(result), and you need to set a threshold of minVal to filter unmatched results.

    Example:

    # loop over the scales of the image
    for scale in np.linspace(0.2, 1.0, 20)[::-1]:
        # resize the image according to the scale, and keep track
        # of the ratio of the resizing
        resized = imutils.resize(gray, width=int(gray.shape[1] * scale))
        r = gray.shape[1] / float(resized.shape[1])
    
        # if the resized image is smaller than the template, then break
        # from the loop
        if resized.shape[0] < tH or resized.shape[1] < tW:
            break
    
        # detect edges in the resized, grayscale image and apply template
        # matching to find the template in the image
        edged = cv2.Canny(resized, 50, 200)
        result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF)
        (minVal, maxVal, _, maxLoc) = cv2.minMaxLoc(result)
    
        # if we have found a new maximum correlation value, then ipdate
        # the bookkeeping variable
        if found is None or maxVal > found[0]:
            found = (maxVal, maxLoc, r)
    
    # unpack the bookkeeping varaible and compute the (x, y) coordinates
    # of the bounding box based on the resized ratio
    (maxVal, maxLoc, r) = found
    # Threshold setting, this 11195548 value is tested by some random images
    threshold = 11195548
    if maxVal > threshold:
        print("match found")
        (startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
        (endX, endY) = (int((maxLoc[0] + tW) * r), int((maxLoc[1] + tH) * r))
    
        # draw a bounding box around the detected result and display the image
        cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
        cv2.imshow("Image", image)
        cv2.waitKey(0)
    else:
        print("no match found")