21

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?

Python1c
  • 221
  • 1
  • 2
  • 10

3 Answers3

25

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.

Sunreef
  • 4,452
  • 21
  • 33
2

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()
fam
  • 583
  • 3
  • 14
  • How will this help? Inverting depends on the image. You must analyze the hierarchy to remove contours within contour for a more general solution – Jeru Luke Jun 10 '22 at 15:20
  • 1
    Yes, that is true. Inverting depends on the image. However, my answer is in the light of documentation: "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". Therefore, we have to make conversions depending upon the color representing our object and the color representing the background. – fam Jun 13 '22 at 05:32
  • you have shared both sides of the coin +1 – Jeru Luke Jun 14 '22 at 21:56
-1
img_output, contours, hierarchy = cv2.findContours(blank_image_firstImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

This removes the child contour

Subhamp7
  • 29
  • 5
  • 1
    this only finds outer contours, does not remove contours inside contours. if there is one big contour for whole image, then it returns only that one. – Erdogan Kurtur Nov 14 '20 at 18:45
  • Thanks! This solved my problem which is related to the original question, by fixing the step that caused the problem in the first place. – Laurin Herbsthofer Dec 09 '20 at 10:45