How to remove an object from an image with Python

Stokry - Oct 9 '20 - - Dev Community

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


Enter fullscreen mode Exit fullscreen mode

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):


Enter fullscreen mode Exit fullscreen mode

then we grab the image size and initialize dimensions



dim =  None
(h, w)  = image.shape[:2]


Enter fullscreen mode Exit fullscreen mode

then we return original image if no need to resize:



if width is  None  and height is  None:
    return image


Enter fullscreen mode Exit fullscreen mode

We are resizing height if width is none



if width is  None:
    r = height /  float(h)
    dim =  (int(w * r), height)


Enter fullscreen mode Exit fullscreen mode

We are resizing width if height is none



else:
    r = width /  float(w)
    dim =  (width,  int(h * r))


Enter fullscreen mode Exit fullscreen mode

Return the resized image



return cv2.resize(image, dim,  interpolation=inter)


Enter fullscreen mode Exit fullscreen mode

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)


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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)


Enter fullscreen mode Exit fullscreen mode

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))


Enter fullscreen mode Exit fullscreen mode

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)


Enter fullscreen mode Exit fullscreen mode

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)


Enter fullscreen mode Exit fullscreen mode

Original image:

enter image description here

When we run the script, we get this result

enter image description here

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)


Enter fullscreen mode Exit fullscreen mode

Thank you all

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .