I am just starting to play with OpenCV and I have found some very strange behaviour from the contourArea function.
See this image.
[![Original][1]][1]
It has three non connected areas, the left is a grouping of long strokes and on the top center there is a single dot and finally a big square on the right.
When I run my function, I get this result
[![Contours detected][2]][2]
Contour[0] Area: 221, Length: 70, Colour: Red
Contour[1] Area: 13772, Length: 480, Colour: Green
Contour[2] Area: 150, Length: 2370, Colour: Blue
While I havent actually counted the area of the left part, It seems as if it encompasses much more than 149 pixels and would certainly have a higher value than the dot in the top center, I would say that dot should be able to fit in to the left part at least 10 times. The area of the square does work out, I did calculate the area of the square.
Square Area
width = 118
height = 116
118 * 116 = 13,688
13,688 is really close to what opencv gave as the area, the difference is likely measurement error on my behalf.
I manually calculated the area of the dot
Dot Area
width = 27
height = 6
27*6 = 162
Not too far off from what opencv said it would be
Reading from the [OpenCV docs page on contourArea][3] it says that it will give wrong results for contours with self intersections. Not really understanding what self intersections are, I made a test image.
[![ORIGINAL PIC HERE][4]][4]
As you can see I have a rectangle on the left and a cross in the middle and another cross rotated 45 deg.
I would expect the cross to have slightly less than double the area of the rectangle due to the overlap in the center.
[![CONTOURED PIC HERE][5]][5]
Contour[0] Area: 1805, Length: 423, Colour: Red
Contour[1] Area: 947, Length: 227, Colour: Green
Contour[2] Area: 1825, Length: 415, Colour: Blue
As you can see the area of the two crosses are slightly less than double the area of the rectangle. As expected.
I am not interested in capturing the inside of the square or getting a box drawn around the shape on the left and the dot (though it would be tangentially interesting) it's not specifically what I'm asking about in this question.
So my question:
Why is the area of my irregular shape severly underestimated?
1. Am I using the wrong function?
2. Am I using the right function incorrectly?
3. Have I found a bug in opencv?
4. Does self intersections have a meaning that wasn't demonstrated in my test?
I copied most of this code from [this tutorial][6]
I have stripped down my code to this self contained example below.
def contour_test(name):
import cv2 as cv
colours = [{'name': 'Red ', 'bgr': (0, 0, 255)},
{'name': 'Green ', 'bgr': (0, 255, 0)},
{'name': 'Blue ', 'bgr': (255, 0, 0)}]
src = cv.imread(cv.samples.findFile(name))
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
src_gray = cv.blur(src_gray, (3,3))
threshold = 100
canny_output = cv.Canny(src_gray, threshold, threshold * 2)
contours, _ = cv.findContours(canny_output, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# Get the moments
mu = [None for i in contours]
for i in range(len(contours)):
mu[i] = cv.moments(contours[i])
# Get the mass centers
mc = [None for i in contours]
for i in range(len(contours)):
mc[i] = (mu[i]['m10'] / (mu[i]['m00'] + 1e-5), mu[i]['m01'] / (mu[i]['m00'] + 1e-5))
# Draw contours
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
for i, j in enumerate(contours):
colour = colours[i]['bgr']
cv.drawContours(drawing, contours, i, colour, 2)
area = int(cv.contourArea(contours[i]))
length = int(cv.arcLength(contours[i], True))
print('Contour[{0}] Area: {1}, Length: {2}, Colour: {3}'.format(i, area, length, colours[i]['name']))
[1]: https://i.stack.imgur.com/457SN.jpg
[2]: https://i.stack.imgur.com/jN88B.jpg
[3]: https://docs.opencv.org/master/d3/dc0/group__imgproc__shape.html#ga2c759ed9f497d4a618048a2f56dc97f1
[4]: https://i.stack.imgur.com/ohbOF.jpg
[5]: https://i.stack.imgur.com/tqUL6.jpg
[6]: https://docs.opencv.org/master/d0/d49/tutorial_moments.html
↧