Reputation: 1518
Tl;DR: How to measure area enclosed by contour rather than just the contour line itself
I want to find the outline of the object in the below image and have a code that works for most cases.
Thresholding and adpative thresholding do not work reliably as the ligthing changes. I use a Canny edge detection and check the area to ensure I found the proper contour. However, once in a while, when there is a gap that cannot be closed by morphological closing, the shape is correct but the area is of the contour line instead of the whole object.
What I usually do is use convexHull, as it returns a contour around the object. However, in this case the object curves inwards along the top and convexHull isn't a good approximation to the area anymore.
I tried using approxPolyDP but the area that gets returned is of the contour line rather than the object.
How can I get the approxPolyDP to return a similar closed contour around the object, just like the convexHull function does?
Code illustrating this using the above picture:
import cv2
img = cv2.imread('Img_0.jpg',0)
cv2.imshow('Original', img)
edges = cv2.Canny(img,50,150)
cv2.imshow('Canny', edges)
contours, hierarchy = cv2.findContours(edges,cv2.cv.CV_RETR_EXTERNAL,cv2.cv.CV_CHAIN_APPROX_NONE)
cnt = contours[1] #I have a function to do this but for simplicity here by hand
M = cv2.moments(cnt)
print('Area = %f \t' %M['m00'], end="")
cntHull = cv2.convexHull(cnt, returnPoints=True)
cntPoly=cv2.approxPolyDP(cnt, epsilon=1, closed=True)
MHull = cv2.moments(cntHull)
MPoly = cv2.moments(cntPoly)
print('Area after Convec Hull = %f \t Area after apporxPoly = %f \n' %(MHull['m00'], MPoly['m00']), end="")
x, y =img.shape
size = (w, h, channels) = (x, y, 1)
canvas = np.zeros(size, np.uint8)
cv2.drawContours(canvas, cnt, -1, 255)
cv2.imshow('Contour', canvas)
canvas = np.zeros(size, np.uint8)
cv2.drawContours(canvas, cntHull, -1, 255)
cv2.imshow('Hull', canvas)
canvas = np.zeros(size, np.uint8)
cv2.drawContours(canvas, cntPoly, -1, 255)
cv2.imshow('Poly', canvas)
The output from the code is
Area = 24.500000 Area after Convec Hull = 3960.500000 Area after apporxPoly = 29.500000
Upvotes: 3
Views: 3908
Reputation: 11075
Here's a very promising ppt from geosensor.net that discusses several algorithms. My recommendation would be to use the swing arm method with a limited radius.
Another completely un-tested, off the wall idea I have is to scan across the image by row and column (more directions increase accuracy) and color in the regions between line intersections:
_______
/-------\
/---------\
--------+---------+------ (fill between 2 intersections)
| |
|
--------+---------------- (no fill between single intersection)
\
-------
the maximum error would then decrease as the number of line directions scanned increases (more than 90 and 45 degrees). Getting a final area would then be as simple as a pixel count.
Upvotes: 1