Getting duplicate contours

Question:

I want to extract the number of contours/objects in each image along with its side i-e a function should return [num_contours, total_sides, (sides of individual contours)]

But i’m getting two contours for each shape (outer and inner both).

Original image
Contour 1
Contour 2
Contour 3
Contour 4

My function:

def get_contour_details(img):
    image = img.copy()
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    value, thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY_INV)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    contour_edges = [len(cv2.approxPolyDP(contour, 0.01* cv2.arcLength(contour, True), True)) for contour in contours]
    num_contours = len(contours)
    total_edges = sum(contour_edges)
    return num_contours, total_edges, contour_edges

Expected answer: [2, 8, [4,4]]

Got: [4, 18, [4, 4, 4, 6]]

Use below image for processing:

enter image description here

Any kind of help will be appreciated!

Asked By: Hamza usman ghani

||

Answers:

Open cv will recognize and return both inner and outer contours. You need to filter them based on your need. The relation between contours can be deduced from hierarchy. Hierarchy returns the following list,

[next, previous, first_child, parent]
# next and previous are with respect to same hierarchy level.

If you are interested in external contours, the rows with no parent ( parent = -1) show the external ones. If you need the internal contours the parents will be a nonnegative number (denoting the index of parent in the contours list).

The following code (that I used " cv2.RETR_CCOMP" flag for heirarchy retrieval for more simplicity as it only gives two level hierarchy) will do the job based on considering external contours and is tested on your sample image.

import numpy as np
import cv2

def get_contour_details(img):
    image = img.copy()
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    value, thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY_INV)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
    # Find external contours.
    index = np.argwhere(hierarchy[0,:,3] == -1).flatten()
    contours = [contours[i] for i in index]
    #
    contour_edges = [len(cv2.approxPolyDP(contour, 0.01* cv2.arcLength(contour, True), True)) for contour in contours]
    num_contours = len(contours)
    total_edges = sum(contour_edges)
    return num_contours, total_edges, contour_edges

img = cv2.imread('img.png', cv2.IMREAD_COLOR)
print(get_contour_details(img))

For learning better about contour hierarchy and its different types see Hierarchy explanation from opencv official tutorial.

Answered By: Farshid

You are using the wrong retrieval mode. Why did you choose RETR_TREE ? According to the documentation:

RETR_TREE retrieves all of the contours and reconstructs a full
hierarchy of nested contours

So your code is finding both contours inner and outer. Using RETR_EXTERNAL can fix your problem. Because it will only draw the outer contour. By referring to the documentation again:

RETR_EXTERNAL retrieves only the extreme outer contours.

More better option is using RETR_CCOMP which creates a hierarchy for all of the contours. Parent-child relationship, so you can easily separate the contours.

Answered By: Yunus Temurlenk