Today I want to show you a sweet algorithm with which you can remove objects from the picture. For example, if we have thousands of images where we have some objects that we want to delete, this algorithm can help us complete this task.
We will be using modified Template Matching approach.
We will load the template, convert to grayscale, perform canny edge detection, after that we do load the original image, convert to grayscale
Continuously rescale the image, apply template matching using edges, and keep track of the correlation coefficient (higher value means better match)
Find coordinates of best-fit bounding box then erase unwanted ROI
We'll use the cv2 module and NumPy. You can read about them on these URLs.
We'll use the cv2
module and NumPy
. You can read about them on these URLs, CV2, and Numpy.
Let's start coding
First we will import a module
import cv2
import numpy as np
After that we do resize a image and maintain aspect ratio
def maintain_aspect_ratio_resize(image, width=None, height=None, inter=cv2.INTER_AREA):
then we grab the image size and initialize dimensions
dim = None
(h, w) = image.shape[:2]
then we return original image if no need to resize:
if width is None and height is None:
return image
We are resizing height if width is none
if width is None:
r = height / float(h)
dim = (int(w * r), height)
We are resizing width if height is none
else:
r = width / float(w)
dim = (width, int(h * r))
Return the resized image
return cv2.resize(image, dim, interpolation=inter)
Load template, convert to grayscale, perform canny edge detection
template = cv2.imread('template.png')
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]
cv2.imshow("template", template)
Load original image, convert to grayscale
original_image = cv2.imread('test.png')
final = original_image.copy()
gray = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
found = None
Dynamically rescale image for better template matching
for scale in np.linspace(0.2, 1.0, 20)[::-1]:
resized = maintain_aspect_ratio_resize(gray, width=int(gray.shape[1] * scale))
r = gray.shape[1] / float(resized.shape[1])
if resized.shape[0] < tH or resized.shape[1] < tW:
break
canny = cv2.Canny(resized, 50, 200)
detected = cv2.matchTemplate(canny, template, cv2.TM_CCOEFF)
(_, max_val, _, max_loc) = cv2.minMaxLoc(detected)
if found is None or max_val > found[0]:
found = (max_val, max_loc, r)
Compute coordinates of bounding box
(_, max_loc, r) = found
(start_x, start_y) = (int(max_loc[0] * r), int(max_loc[1] * r))
(end_x, end_y) = (int((max_loc[0] + tW) * r), int((max_loc[1] + tH) * r))
Draw bounding box on ROI to remove
cv2.rectangle(original_image, (start_x, start_y), (end_x, end_y), (0,255,0), 2)
cv2.imshow('detected', original_image)
Erase unwanted ROI (Fill ROI with white)
cv2.rectangle(final, (start_x, start_y), (end_x, end_y), (255,255,255), -1)
cv2.imwrite('final.png', final)
cv2.waitKey(0)
Original image:
When we run the script, we get this result
Whole code:
import cv2
import numpy as np
def maintain_aspect_ratio_resize(image, width=None, height=None, inter=cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
return cv2.resize(image, dim, interpolation=inter)
template = cv2.imread('template.png')
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]
cv2.imshow("template", template)
original_image = cv2.imread('test.png')
final = original_image.copy()
gray = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
found = None
for scale in np.linspace(0.2, 1.0, 20)[::-1]:
resized = maintain_aspect_ratio_resize(gray, width=int(gray.shape[1] * scale))
r = gray.shape[1] / float(resized.shape[1])
if resized.shape[0] < tH or resized.shape[1] < tW:
break
canny = cv2.Canny(resized, 50, 200)
detected = cv2.matchTemplate(canny, template, cv2.TM_CCOEFF)
(_, max_val, _, max_loc) = cv2.minMaxLoc(detected)
if found is None or max_val > found[0]:
found = (max_val, max_loc, r)
(_, max_loc, r) = found
(start_x, start_y) = (int(max_loc[0] * r), int(max_loc[1] * r))
(end_x, end_y) = (int((max_loc[0] + tW) * r), int((max_loc[1] + tH) * r))
cv2.rectangle(original_image, (start_x, start_y), (end_x, end_y), (0,255,0), 2)
cv2.imshow('detected', original_image)
cv2.rectangle(final, (start_x, start_y), (end_x, end_y), (255,255,255), -1)
cv2.imwrite('final.png', final)
cv2.waitKey(0)
Thank you all
Top comments (11)
Can you please explain...How can we remove duplicate objects in a single image?
This is an example: pyimagesearch.com/2015/02/09/remov...
Also, In this example, our goal is to remove the circles/ellipses from the image. But my requirement is to Remove the duplicate objects from the image using template matching techniques.
This is a nice tutorial: towardsdatascience.com/object-dete...
Thanks for your reply, But I need the different requirements. This link finds the duplicate by contours. but I need to find the multiple objects using the template matching techniques
Already, I derived the code, the problem is..I have one template to find the matching object in that image..I have totally 5 duplicates but my system shows 6 duplicates one is wrong identifying..Am trying to fix it. I have your support it will better
Hi,Nice Explanation
Thanks!
Now I know how they got rid of Daenerys' Starbucks cup!
Can you please give some idea to remove all the matching objects from the original image using python and OpenCV method or Template matching techniques?
I'm just finishing up a similar article, and I'm about to publish it on the dev.to.