How to remove an object from an image with Python
Tags: python, productivity, showdev, tutorial
Published: 2020-10-09 10:22:13

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](https://docs.opencv.org/trunk/d4/dc6/tutorial_py_template_matching.html) 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](https://pypi.org/project/opencv-python/),  and [Numpy](https://numpy.org/).

Let's start coding
First we will import a module
```shh
import cv2
import numpy as np
```
After that we do resize a image and maintain aspect ratio
```shh
def  maintain_aspect_ratio_resize(image,  width=None,  height=None,  inter=cv2.INTER_AREA):
```
then we grab the image size and initialize dimensions

```shh
dim =  None
(h, w)  = image.shape[:2]
```
then we return original image if no need to resize:
```shh
if width is  None  and height is  None:
    return image
```
We are resizing height if width is none

```shh
if width is  None:
    r = height /  float(h)
    dim =  (int(w * r), height)
```
We are resizing width if height is none
```shh
else:
    r = width /  float(w)
    dim =  (width,  int(h * r))
```
Return the resized image
```shh
return cv2.resize(image, dim,  interpolation=inter)
```
Load template, convert to grayscale, perform canny edge detection

```shh
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
```shh
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
```shh
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
```shh
(_, 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

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

```shh
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:

![enter image description here](https://i.ibb.co/gWxPQ6n/org.png)

When  we  run  the  script,  we  get  this  result

![enter image description here](https://i.ibb.co/sRf5fNk/final.png)

Whole code:

```shh
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***