[英]Open CV Contour area miscalculation
I am just starting to play with OpenCV and I have found some very strange behaviour from the contourArea function.我刚开始玩 OpenCV,我发现轮廓区域 function 有一些非常奇怪的行为。
See this image.看到这张图片。
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当我运行我的 function 时,我得到了这个结果
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 150 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.虽然我实际上没有计算左侧部分的面积,但它似乎包含超过 150 个像素,并且肯定会比顶部中心的点具有更高的值,我会说点应该能够适应左侧部分至少 10 次。 The area of the square does work out.广场的面积确实有效。
Square Area
width = 118
height = 116
118 * 116 = 13,688
13,688 is really close to what opencv gave as the area (13,772), the difference is likely measurement error on my behalf. 13,688 非常接近 opencv 给出的面积 (13,772),差异可能代表我的测量错误。 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 (221)与 opencv 所说的相差不远(221)
Reading from the OpenCV docs page on contourArea it says that it will give wrong results for contours with self intersections.从contourArea 上的 OpenCV 文档页面读取,它说它将为具有自相交的轮廓提供错误的结果。 Not really understanding what self intersections are, I made a test image.不太了解自交点是什么,我制作了一个测试图像。
As you can see I have a rectangle on the left and a cross in the middle and another cross rotated 45 deg.如您所见,我在左侧有一个矩形,中间有一个十字,另一个十字旋转了 45 度。 I would expect the cross to have slightly less than double the area of the rectangle due to the overlap in the center.由于中心的重叠,我希望十字架的面积略小于矩形面积的两倍。
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?所以我的问题是:为什么我的不规则形状的面积被严重低估了?
I copied most of this code from this tutorial我从本教程中复制了大部分代码
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']))
The inner part of the contours the findContours
finds is supposed to be of filled with white color. findContours
找到的等高线的内部应该是用白色填充的。
cv.Canny
before findContours
( cv.blur
is also not required).不要在findContours
之前使用cv.Canny
(也不需要cv.blur
)。cv.threshold
with cv.THRESH_BINARY_INV
option for inverting polarity.您可以使用cv.threshold
和cv.THRESH_BINARY_INV
选项来反转极性。cv.THRESH_OTSU
option for automatic threshold.建议为自动阈值添加cv.THRESH_OTSU
选项。 You may replace cv.blur
and cv.Canny
and cv.findContours(canny_output...
with:您可以将cv.blur
和cv.Canny
和cv.findContours(canny_output...
替换为:
_, src_thresh = cv.threshold(src_gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
contours, _ = cv.findContours(src_thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
Result (of top image):结果(顶部图像):
Contour[0] Area: 13531, Length: 476, Colour: Red
Contour[1] Area: 184, Length: 71, Colour: Green
Contour[2] Area: 4321, Length: 1202, Colour: Blue
Here is the complete (updated) code:这是完整的(更新的)代码:
import numpy as np
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)
_, src_thresh = cv.threshold(src_gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
cv.imshow('src_thresh', src_thresh);cv.waitKey(0);cv.destroyAllWindows() # Show src_thresh for testing
contours, _ = cv.findContours(src_thresh, 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((src_thresh.shape[0], src_thresh.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']))
cv.imshow('drawing', drawing);cv.waitKey(0);cv.destroyAllWindows() # Show drawing for testing
contour_test('img.jpg')
I added cv.imshow
in two places for testing.我在两个地方添加了cv.imshow
进行测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.