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).
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:
Any kind of help will be appreciated!
CodePudding user response:
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 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.
CodePudding user response:
You are using the wrong retrieval mode. Why did you choose RETR_TREE
? According to the documentation:
CV_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:
CV_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 elationship, so you can easily seperate the contours.