The problem statement is basically shape detection, when there is a shape inside another, and also list the hierarchy order, for example, "triangle inside hexagon". For this, I am using the numpy array that cv.findContours gives me. The problem I am encountering is that the hierarchial relationships are just not accurate at all, it just doesn't work and I am not able to figure out why. It would be great if someone could tell me if there is a problem in the fundamental logic in my code or if it's just a tweaking issue. I have attached my code below.
I am using opencv 4.2
I have also attached the image I am using below

The image that I get in output is

Also the output I get in console is
hexagon inside hexagon
hexagon inside hexagon
hexagon inside hexagon
hexagon inside hexagon
{1: {'id': 1, 'shape': 'hexagon', 'hierarchy_shit': array([-1, -1, 2, 0], dtype=int32), 'cx': 311, 'cy': 300, 'area': 193872.0}, 2: {'id': 2, 'shape': 'hexagon', 'hierarchy_shit': array([-1, -1, 3, 1], dtype=int32), 'cx': 311, 'cy': 300, 'area': 191480.0}, 3: {'id': 3, 'shape': 'hexagon', 'hierarchy_shit': array([-1, -1, 4, 2], dtype=int32), 'cx': 309, 'cy': 299, 'area': 189085.5}, 5: {'id': 5, 'shape': 'triangle', 'hierarchy_shit': array([7, 4, 6, 3], dtype=int32), 'cx': 357, 'cy': 281, 'area': 9062.5}, 6: {'id': 6, 'shape': 'triangle', 'hierarchy_shit': array([-1, -1, -1, 5], dtype=int32), 'cx': 356, 'cy': 281, 'area': 8902.0}}
import cv2 as cv
import numpy as np
capture = cv.imread("img.png")
gray = cv.cvtColor(capture, cv.COLOR_BGR2GRAY)
blurred = cv.GaussianBlur(gray, (3, 3), cv.BORDER_DEFAULT)
canny = cv.Canny(blurred, 50, 150, apertureSize=3)
contours, hierarchy = cv.findContours(canny, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.imshow('frame', canny)
cv.drawContours(capture,contours, -1, (255,0,0))
ldic = {}
for idx,contour in enumerate(contours):
if idx == 0:
continue
if cv.contourArea(contour) < 1200:
continue
epsilon = 0.01*cv.arcLength(contour, True)
approx = cv.approxPolyDP(contour, epsilon, True)
M = cv.moments(approx)
sides = len(approx)
if sides == 3:
label = "triangle"
elif sides == 4:
label = "square"
elif sides == 5:
label = "pentagon"
elif sides == 6:
label = "hexagon"
else:
continue
cont_dict = {
"id":idx,
"shape":label,
"hierarchy_shit":hierarchy[0][idx],
"cx":int(M['m10']/M['m00']),
"cy": int(M['m01'] / M['m00']),
"area": M['m00']
}
ldic[idx] = cont_dict
if M['m00'] != 0:
cx = int(M['m10']/M['m00'])
cy = int(M['m01'] / M['m00'])
cv.putText(capture, cont_dict["shape"], (cx, cy), cv.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 3, cv.FILLED)
for key,val in ldic.items():
parent_idx = int(val["hierarchy_shit"][3])
if parent_idx != -1 and parent_idx in ldic and val["cx"]-ldic[val["hierarchy_shit"][3]]["cx"] >= 0 and val["cy"]-ldic[val["hierarchy_shit"][3]]["cy"] >= 0:
cv.putText(capture, f"{val["shape"]} inside {ldic[val["hierarchy_shit"][3]]["shape"]}",
(cx, cy), cv.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 3, cv.FILLED)
print(f"{val["shape"]} inside {ldic[val["hierarchy_shit"][3]]["shape"]}")
cv.imshow('frame1', capture)
print(ldic)
cv.waitKey(0)
cv.destroyAllWindows()