miguel
miguel

Reputation: 11

Compute an area with cv2.contourArea

Computing the area of a contour with cv2.contourArea() doesn't provide to me the right figure. I've tried the following very simple example for a square and a circle but the results are not the expected ones. Any cue about what I'm wrongly doing ?

import cv2
import numpy as np

image = np.zeros((512, 5122,3), np.uint8)
cv2.rectangle(image, (100,100), (200,200), (0,255,0),2 )
cv2.circle(image, (256, 256), 10, (0, 255, 0), 2)
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(image_gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
superficie = [cv2.contourArea(cnt) for cnt in contours]
perimeter = [cv2.arcLength(cnt, True) for cnt in contours]

for i in range(0, len(superficie)):
    x, y, w, h = cv2.boundingRect(contours[i])
    print(f'contour {i} area: {superficie[i] } bounding area {w*h}  perimeter: {round(perimeter[i],2)}')

The result for the square (contour 3) and the circle (contour 1) are not THE EXPECTED ONES:

contour 0 area: 248.0 bounding area: 361 perimeter: 57.94 contour 1 area: 386.0 bounding area: 529 perimeter: 73.94 contour 2 area: 9602.0 bounding area: 9801 perimeter: 389.66 contour 3 area: 10402.0 bounding area: 10609 perimeter: 405.66

I've tried to get the value for the area of the square equals to 100100=10000 and it's perimeter equals to 4**100 =400. However, I get 10402 for the area and 405.66 for the perimeter. Similarly, for the circle I expected PI1010 = 314.1 and I get 386 and for the perimeter I expected 2PI10 = 62.8 and I get 73.94

Also I get different values for a bounding rectangle of the square

Upvotes: 1

Views: 3066

Answers (1)

I don't quite fully understand the code you've uploaded, but make sure you take into account some features of cv2.contourArea().

  1. The first one is that cv2.contourArea() doesn't return the exact area in the way we calculate areas for real-continuous things. The value it returns is the number of pixels that are inside the correspondent contour. So, while with real things you can get an area with infinite decimal precision, in this approach they might be some approximation errors, because the circle line isn't a perfect continuous line, actually it is line with little steps.

  2. The second thing is that cv2.findContours() doesn't return the exact line that is plotted with cv2.rectangle() or cv2.circle(). This function search for the pixels for which there is a very high difference in their value with their respective neighbors. This result in two contours for every simple line/figure: one for the external part of the line and another for the internal part of the line. If you could zoom in, you could see that cv2.findContours return two of this contours because in the external line of the figure you have a very high value change between the white and the black pixels, and another one for the internal.

With this approach, you get different areas if you calculate it with the external or the internal contour.

contours, _ = cv2.findContours(image_with_circle_bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
image_with_con = cv2.drawContours(cv2.cvtColor(image_with_circle_bw.copy(), cv2.COLOR_GRAY2BGR), contours, 1, (0,0,255), 4)

area_1 = cv2.contourArea(contours[0])
area_2 = cv2.contourArea(contours[1])
area = (area_1+area_2)/2
print(area_1) # Area of the external bound
print(area_2) # Area of the internal bound
print(area) # Mean of the two areas
print(200**2*math.pi) # 200 is because radius is 200

This prints the following:

128058.0 # Area of the external bound
122939.0 # Area of the internal bound
125498.5 # Mean of the two areas
125663.70614359173 # Real area of a circle with radius = 200

So as you can see, to obtain an approximation, I calculated the mean between the two areas. For sure this isn't the better way to approximate this but it gets really close with the real one. It is needed to understand that contourArea() won't return the real value of the area, it will return a higher or a lower value, because it doesn't search for the actual line, it searchs for the bound-pixels, where black and white pixels are next to each other.

Upvotes: 0

Related Questions