Python1c
Python1c

Reputation: 213

How to remove a contour inside contour in Python OpenCV?

OpenCV in Python provides the following code:

regions, hierarchy = cv2.findContours(binary_image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)


for region in regions:
    x, y, w, h = cv2.boundingRect(region)

    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 1)

This gives some contours within contour. How to remove them in Python?

Upvotes: 21

Views: 21041

Answers (3)

fam
fam

Reputation: 711

In order to remove the contours inside a contour:

shapes, hierarchy = cv2.findContours(image=image, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)

However, in some cases you may observe that a big contour is formed on the whole image, and applying the above returns you that one big contour.

In order to avoid this, try inverting the image:

image = cv2.imread("Image Path")
image = 255 - image
shapes, hierarchy = cv2.findContours(image=image, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)

This will give you the desired result.

UPDATE:

The reason why hierarchy does not work if a big bounding box is approximated on the whole image is that the output of hierarchy[0,iteration,3] is -1 only for the one bounding box drawn on the whole image, as all other bounding boxes are inside this big bounding box, and hierarchy[0,iteration,3] is not equal to -1 for any of them. Thus, inverting the image will be required in order to comply with the following:

In OpenCV, finding contours is like finding white object from black background. So remember, object to be found should be white and background should be black.

However, as pointed out by @Jeru, this is not a generalized solution and one must visualize the image before inverting it. Consider this image: enter image description here

Running

shapes, hierarchy = cv2.findContours(image=image, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)

results in

enter image description here

Now, only displaying the contour with hierarchy[0,iteration,3] = -1 results in

enter image description here

which is not correct. If we want to obtain the rectangle containing the shapes and the text shapes, we can do

image = 255 - image
shapes, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)

In this case we get:

enter image description here

Code:

import cv2
from easyocr import Reader
import math


shape_number = 2

image = cv2.imread("Image Path")
deep_copy = image.copy()

image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(image_gray, 210, 255, cv2.THRESH_BINARY)
thresh = 255 - thresh

shapes, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image=deep_copy, contours=shapes, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)

for iteration, shape in enumerate(shapes):

        if hierarchy[0,iteration,3] == -1:
                print(hierarchy[0,iteration,3])
                print(iteration)

cv2.imshow('Shapes', deep_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

Upvotes: 2

Subhamp7
Subhamp7

Reputation: 59

img_output, contours, hierarchy = cv2.findContours(blank_image_firstImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

This removes the child contour

Upvotes: -1

Sunreef
Sunreef

Reputation: 4542

For that, you should take a look at this tutorial on how to use the hierarchy object returned by the method findContours .

The main point is that you should use cv2.RETR_TREE instead of cv2.RETR_LIST to get parent/child relationships between your clusters:

regions, hierarchy = cv2.findContours(binary_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

Then you can check whether a contour with index i is inside another by checking if hierarchy[0,i,3] equals -1 or not. If it is different from -1, then your contour is inside another.

Upvotes: 25

Related Questions