GrammarsThough
GrammarsThough

Reputation: 11

OpenCV template matching True/False

So I don't know if it's easy to understand from the title, but my problem has to do with OpenCV template matching, and when the program could say 'Template found' and return a boolean value for while loops. P.S I know saving the image and deleting it all the time is very inefficient, but it works

import cv2
import numpy as np
from PIL import ImageGrab
import os

the original images function is just a way to get Pil image converted to something opencv can work with

def originalImages():
    screenPart = ImageGrab.grab(bbox=(900,40,1030,105))
    screenPart.save('CurrentFrame.jpg','JPEG')
    screen = cv2.imread('CurrentFrame.jpg', 0)
    cv2.imshow('screen',screen)
    os.remove('CurrentFrame.jpg')
    return screen

screen = originalImages()
res = cv2.matchTemplate(screen, template_img, cv2.TM_CCOEFF_NORMED)
loc = np.where(res>=0.8) #0.8 is the threshold

Then the point in zip location, is just to draw where it was matched.

for pt in zip(*loc[::-1]):
    cv2.rectangle(screen, pt, (pt[0] + w, pt[1] + h),(0,255,0) , 2)

So my question is that at what point could I use a if statement to determine that something is indeed matched at that point and execute the program that should come after finding something.

!EDIT! To clear a misconception, i don't require the location nor the drawing of the detected object, i just need a boolean value that tells if something was found or not

Upvotes: 1

Views: 8097

Answers (4)

Akali DZ
Akali DZ

Reputation: 49

I geuss its late now but am facing the same prblm as you. The thing is that cv2 in your case will always find multiple matches , doesnt matter even if you put the threshhold at 100%, the reason is your image that you wanna find is not very unique so opencv will miss match, try to use hsv masks and some other filters to narrow the possibilities maybe you will find a one match up in your thresh hold. I hope my idea is clear. A simple bypass is to use pyautogui locateonscreen function to dot is as it will return a coordinates of found object or null. Good luck.

Upvotes: 0

kekpirat
kekpirat

Reputation: 405

Jeru Luke's code didnt work for me (as pointed out in the comments). My slight modification fixed it:

threshold = 0.8
flag = False
if np.amax(res) > threshold:
    flag = True

Upvotes: 3

Nicolas
Nicolas

Reputation: 21

So I think you need a statistical approach. If you have many images (N>30) and one template, matchTemplate + minMaxLoc will give you on average a (perhaps positive) number. You need to define a threshold over which a match is True match.

One alternative is to check if a match is an outlier in a statistical sense. For that, I would follow this procedure:

1) Match the template to each image and recover the best match per image (minMaxloc). Collect all in a list. 2) Calculate the average and standard deviation (np.mean() and np.std()) for that list. 3) Calculate the standardised "score": z = (rho - mean(rho))/std(rho) if N>120, or the t-stat = (rho-mean(rho))/(std(rho)/sqrt(N-1)) if N<120. 4) Compare with the appropriate critical value at a desired significance level.

This way you will detect which matches are outliers.

Cheers, Nicolás

Upvotes: 0

Jeru Luke
Jeru Luke

Reputation: 21203

If you want to obtain the most prominent match in the image you can use cv2.minMaxLoc() function to obtain the location exhibiting the maximum match:

res = cv2.matchTemplate(screen, template_img, cv2.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(res)

top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)

cv2.rectangle(screen, top_left, bottom_right, (0, 255, 0), 2)
cv2.imshow('Result', screen)   

The desired result would be a bounding box around the region with highest match like the following:

enter image description here

EDIT

Based on the query, the idea is to get a Boolean result deciding whether a match was found or not. You can set a threshold and set the flag if any of the values in the result has surpassed the threshold.

For the case above I set the threshold to be 0.8:

threshold = 0.8
flag = False
for i in res:
    if i.any() > threshold:
        flag = True

Upvotes: 4

Related Questions