I'm working with Python and cv2 to try an build a script to find an image inside another image. This is a simplified version of the script I'm working on.
It works fine if the EXACT image used as reference is inside the other image i'm searching inside of.
import cv2
def checkimages(img, template):
result = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
min_val = cv2.minMaxLoc(result)[0]
thr = 10000
return min_val <= thr
template = cv2.imread('logo3.png')
images = ['withlogo.png','withlogo2.png', 'nologo.png']
for image in images:
print('-------------------------------------')
if checkimages(cv2.imread(image), template):
print('{}: {}'.format(image, 'Logo found.'))
else:
print('{}: {}'.format(image, 'No Logo.'))
I'm using TM_SQDIFF of the matchTemplate function to try and get an aprox value and see how likely it is the template image is inside of the other.
As I said, it works fine if the EXACT template image is inside of the other image. But as soon as the template image looks a bit different inside of the other image, it may as well not exist as the result value of the matchTemplate function passes from a 0.0 to 304025248.0. And even more, if I try to find the template in another image that doesn't contain any traces of the template, I also get a massive number, but curiosly it's a lot lower that the previous one that does have a variation of it (104060152.0 for the nologo.png image).
Now, granted, this is the first time I work with cv2, and I may be asking to much of it to find the P logo in other images, without any preprocessing. But I don't understand those matchTemplate results. Do you guys have any recommendations of what I could add or change to make this a bit better.
I realize this won't be fullproof for all images, but I need to get at least an aprox value when the template is inside another image.
------- EDIT 1 - Aug 14 2023
I just made a test script with what @fmw42 commented.
This is the code. Sorry, it's kind of dirty and unoptimized, but what I'm trying to do, using @fmw42 advices is to mask the template and search for it in the main picture. If it's not found I shrink the main pic and check if it can be found in the smaller picture.
import cv2
import numpy as np
confidence_threshold = 0.92
subimage_found = False
def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
dim = None
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), int(height))
else:
r = width / float(w)
dim = (int(width), int(h * r))
print(f'resize dim: {dim}')
resized = cv2.resize(image, dim, interpolation = inter)
return resized
# read image
img = cv2.imread('withlogo2.png')
h, w = img.shape[:2]
# read template with alpha channel
template_with_alpha = cv2.imread('logochico.png', cv2.IMREAD_UNCHANGED)
hh, ww = template_with_alpha.shape[:2]
# extract base template image and alpha channel and make alpha 3 channels
template = template_with_alpha[:,:,0:3]
alpha = template_with_alpha[:,:,2]
alpha = cv2.merge([alpha,alpha,alpha])
# do masked template matching and save correlation image
correlation = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED, mask=alpha)
# get best match
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(correlation)
max_val_corr = '{:.6f}'.format(max_val)
print("correlation score: " + max_val_corr)
print("match location:", max_loc)
max_val_corr = float(max_val_corr)
min_size_reached = False
while not subimage_found and not min_size_reached:
if max_val_corr > confidence_threshold:
print("Subimage found")
subimage_found = True
else:
print("Subimage not found. Resizing...")
for i_ratio in np.arange(0.95, 0.5, -0.05):
new_height = h*i_ratio
print(f"Original height: {h}")
print(f"New height: {new_height}")
resized_image = image_resize(img, height = new_height)
cv2.imshow('resized_image'+str(i_ratio),resized_image)
cv2.waitKey(0)
correlation = cv2.matchTemplate(resized_image, template, cv2.TM_CCORR_NORMED, mask=alpha)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(correlation)
max_val_corr = '{:.6f}'.format(max_val)
max_val_corr = float(max_val_corr)
print("--------------")
print(f'Ratio: {i_ratio}')
print(f"correlation score: {max_val_corr}")
print(f"match location: {max_loc}")
if max_val_corr > confidence_threshold:
print("Subimage found")
subimage_found = True
break
print("Reached min size, no subimage found.")
min_size_reached = True
# draw match
result = img.copy()
if subimage_found:
cv2.rectangle(result, (max_loc), ( max_loc[0]+ww, max_loc[1]+hh), (255,0,255), 1)
cv2.imshow('template',template)
cv2.imshow('alpha',alpha)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
This kinda works.
But not well... the picture is "successfully" found in moto.png, and "successfully" not found in horizon.png... but it isn't found in withlogo2.png either. And the correlation score for horizon.png, even though the image is not found, is extremly high, very close to be a match. for that matter, the correlation score is VERY high with almost all pictures I've used to search for the logo. Thats why the threshold is so high.
I'm guessing I need to do further preprocessing. Any further advice would be greatly appreciated.
Expected Result: the template should only be found when comparing to withlogo2.png. It should return a high correlation score there. It shouldn't be found when comparing to horizon.png.
Actual Result: the template isn't found in either withlogo2.png nor horizon.png, as the threshold is set quite high. If I lower the threshold a bit,the template is found in both. For some reason the correlation score is higher when comparing to horizon.png than comparing to withlogo2.png.
------- EDIT 3 Aug 17th
Sorry, this is the template and the mask that is created (I was having issues with the first template as it did not have a transparency channel):
And these are the results (pink squares)...
As you can see the template is "found" in all 3 images, for all 3 I get a correlation ratio of more than 0.94... but it's all pretty much nonsense... not sure how to correct this.