Object Detection on Python Using Template Matching | by Ravindu Senaratne | Towards Data Science

Whenever we hear about ‘Object Detection’, machine learning along with different frameworks comes to our mind. But we can actually do object detection without using machine learning or any other frameworks. In this article, I’ll show you how to do it using only Python.
We’ll start off by defining a template image(object), and the system is going to find all other objects in the source image that matches the template we picked. So let me explain what I meant by showing you an example. Below there are two pictures, on the left side, we have our source image of airplanes and on the right side, we have our template photo of an airplane as an object.
What I’m gonna do is write a python code to display all the regions where this template image actually fits in my source image.
First, let’s start off by detecting one object, and Secondly, we can adjust our code to detect multiple objects.
For this, we need one source image and one template image. The template image is slide over the source image (like a 2D convolution) and the program will try to find the most accurate match.
Let’s start coding.
import cv2import numpy as npfrom matplotlib import pyplot as pltimg_rgb = cv2.imread('SourceIMG.jpeg')img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)template = cv2.imread('TemplateIMG.jpeg', 0)
In the above part of the code, we are using OpenCV to read our SourceIMG.jpeg and TemplateIMG.jpeg.
height, width = template.shape[::]
When the template image is slid over the source image, it’s looking for the entire region. (top-left corner as the reference frame). After the template image matches the source image, we would like to note the top-left corner location and then draw a box around the region where it actually matches. In order to do that we need to find the height and width of this template image. So we can draw the rectangle.
res = cv2.matchTemplate(img_gray, template, cv2.TM_SQDIFF)
Template matching is part of OpenCV and it takes our grayscale source image and the template image with the statistical metrics we need to use. In this case, I’m using the Minimum Square Difference (TM_SQDIFF) because we are looking for the minimum difference between the template image and the source image.
plt.imshow(res, cmap='gray')
If you plot what we have done so far, we’ll see a probability map. As you can see from the image below, these tiny dots are where the template actually matched.
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
Using the above code we can found out the location of tiny dots in the probability map. Using minMaxLoc(res) we can extract the minimum value, maximum value, minimum location, and maximum location.
top_left = min_loc bottom_right = (top_left[0] + width, top_left[1] + height)cv2.rectangle(img_rgb, top_left, bottom_right, (255, 0, 0), 2)
To draw a blue rectangle on the source image where the template image matches, we need to get the minimum location, min_loc(where match actually starts) as our top left. The same way, we can get our bottom right by top_left[0] + width and top_left[1] + height. With these dimensions, we can draw the blue rectangle using cv2.rectangle. That’s everything we need to set up, let’s go ahead and visualize this.
cv2.imshow("Matched image", img_rgb)cv2.waitKey()cv2.destroyAllWindows()
Full code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
​
img_rgb = cv2.imread('SourceIMG.jpeg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('TemplateIMG.jpeg', 0)
​
height, width = template.shape[::]
​
res = cv2.matchTemplate(img_gray, template, cv2.TM_SQDIFF)
plt.imshow(res, cmap='gray')
​
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
​
top_left = min_loc  #Change to max_loc for all except for TM_SQDIFF
bottom_right = (top_left[0] + width, top_left[1] + height)
cv2.rectangle(img_rgb, top_left, bottom_right, (255, 0, 0), 2) 
​
cv2.imshow("Matched image", img_rgb)
cv2.waitKey()
cv2.destroyAllWindows()
We already did the detecting of one object by selecting the minimum difference between source and template images. By defining a threshold we can detect all the objects that are similar to our template image.
For this, I’ll be using the same source image and the template image I have used for one object detection with a threshold of probability higher than 0.5 (you can decide the threshold value by referring res array). We only need to change just a few lines of code to detect multiple objects.
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
Here I use TM_CCOEFF_NORMED because we need to get the maximum values, not the minimum values. Which means we need to look for multiple objects instead of one.
threshold = 0.5 #For TM_CCOEFF_NORMED, larger values means good fitloc = np.where( res >= threshold)
We are trying to find all the location values which are larger than the threshold value. loc will receive 2 output arrays and combining these arrays we can get the x,y coordinates.
for pt in zip(*loc[::-1]):   cv2.rectangle(img_rgb, pt, (pt[0] + width, pt[1] + height), (255, 0, 0), 1)
What I’m trying to do here is now instead of one we have multiple locations. So for all these locations, we need to draw blue color rectangles. That’s everything we need to set up, let’s go ahead and visualize this.
Full code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
​
img_rgb = cv2.imread('SourceIMG.jpeg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('TemplateIMG.jpeg', 0)
​
height, width = template.shape[::]
​
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
plt.imshow(res, cmap='gray')
​
threshold = 0.5 #For TM_CCOEFF_NORMED, larger values = good fit.
​
loc = np.where( res >= threshold)  
​
for pt in zip(loc[::-1]): 
    cv2.rectangle(img_rgb, pt, (pt[0] + width, pt[1] + height), (255, 0, 0), 1) 
​
cv2.imshow("Matched image", img_rgb)
cv2.waitKey()
cv2.destroyAllWindows()
Seems quite simple right? but we can do this with a higher accuracy if we use machine learning or a framework.
Thank you for reading and I hope this article was helpful.