How to detect a hollowed shape as one object instead of two?

A shape that is hollow, take the below situation as example, is detected as 2 objects, the outer triangle(can be seen as just an outline) is somehow detected as 2 objects as shown in the second image (2 green outline in and outside of the outer black triangle).

First Image:

enter image description here

Second Image (Result from my code):

enter image description here

Expected Result:

enter image description here

My Code:

import os
import cv2
import numpy as np

file_name = os.path.join(os.path.dirname(__file__),'hollowtri.png')
assert os.path.exists(file_name)
a = cv2.imread(file_name)

#Convert image to gray image
imgGray = cv2.cvtColor(a,cv2.COLOR_BGR2GRAY)

#Doing threshold on the image
_,thresh = cv2.threshold(imgGray,100,255,cv2.THRESH_BINARY_INV)
#Finding objects
contours,_ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
#Print the result based on the contours found
num = 0
for contour in contours:
    cv2.drawContours(a,contours,num,(0,255,0),2)
    num+=1

cv2.imshow("Thresh", thresh)
cv2.imshow("Triangle", a)
cv2.waitKey(0)

I don't want to use cv2.RETR_EXTERNAL because I want the inner solid triangle to be detected, so is there a solution to this problem? I want the outer triangle to be detected as just 1 object.

Note: Only the shape colored in black are the object of concerned. So the result should contain only 2 objects, but in image 2, the outer triangle is outlined in green twice, inside and outside, my goal is to outline it just once, because a hollow triangle is just 1 object, not 2.

1 answer

  • answered 2022-05-05 17:56 Jeru Luke

    To solve your problem the choice of flag is important. We are dealing with a situation where there is a contour placed within another contour. We can make use of RETR_CCOMP flag while finding contours cv2.findContours(). And analyze the hierarchy output.

    Intro

    In the following binary image we have one object, but while finding contours we end up with 2 (meaning 2 distinct objects).

    • Contour 1 (pointed in red) is the outer boundary of the object - this is the parent with value -1
    • Contour 2 (pointed in green) is the inner boundary of the object - this is the child with value 0

    enter image description here

    We are interested in finding only the outer boundary of every distinct object. In the image above, that would be only Contour 1.

    Code:

    img = cv2.imread(r'C:\Users\524316\Desktop\Stack\tri.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    th = cv2.threshold(gray,100,255,cv2.THRESH_BINARY_INV)[1]
    
    # using cv2.RETR_CCOMP flag
    contours,hierarchy = cv2.findContours(th,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)
    
    # initializing an index list
    contour_index_list = []
    
    # draw every contour whose 4th column in hierarch array is -1
    for index in range(0, len(contours)):
        if (hierarchy[:,index][0][3] == -1):
            contour_index_list.append(index)
            cv2.drawContours(img, [contours[index]], 0, (0, 255, 0), 3)
    

    Result:

    enter image description here

    Details:

    Observing the hierarchy variable gives an array, with each row assigned to each contour:

    array([[[ 1, -1, -1, -1],
            [-1,  0,  2, -1],
            [-1, -1, -1,  1]]], dtype=int32)
    

    According to the documentation, the 4th column indicates the parent-child relationship. If the value is:

    • -1 the contour is the parent
    • 0 the contour is a child

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum