简体   繁体   English

Open CV Contour 面积计算错误

[英]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?所以我的问题是:为什么我的不规则形状的面积被严重低估了?

  1. Am I using the wrong function?我是否使用了错误的 function?
  2. Am I using the right function incorrectly?我是否错误地使用了正确的 function?
  3. Have I found a bug in opencv?我在 opencv 中发现错误了吗?
  4. Does self intersections have a meaning that wasn't demonstrated in my test?自相交是否具有我的测试中未证明的含义?

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找到的等高线的内部应该是用白色填充的。

  • Don't use cv.Canny before findContours ( cv.blur is also not required).不要在findContours之前使用cv.Canny (也不需要cv.blur )。
  • Make sure the contours are white and not black.确保轮廓是白色而不是黑色。
    You may use cv.threshold with cv.THRESH_BINARY_INV option for inverting polarity.您可以使用cv.thresholdcv.THRESH_BINARY_INV选项来反转极性。
    It is recommended to add 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.blurcv.Cannycv.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM