Reputation: 2243
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!
Upvotes: 2
Views: 266
Reputation: 4342
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.
Upvotes: 2
Reputation: 455
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.
Upvotes: 3