【问题标题】:Open CV Contour area miscalculationOpencv Contour面积计算错误
【发布时间】:2020-07-19 07:57:29
【问题描述】:

我刚开始使用 OpenCV,我发现 contourArea 函数有一些非常奇怪的行为。

看这张图片。

它有三个不相连的区域,左边是一组长笔划,顶部中心有一个点,右边是一个大正方形。

当我运行我的函数时,我得到了这个结果

Contour[0] Area: 221, Length: 70, Colour: Red 
Contour[1] Area: 13772, Length: 480, Colour: Green 
Contour[2] Area: 150, Length: 2370, Colour: Blue 

虽然我没有实际计算左侧部分的面积,但它似乎包含超过 150 个像素,并且肯定比顶部中心的点具有更高的值,我会说点应该能够适合左侧部分至少 10 次。正方形的面积确实计算出来了。

Square Area
width = 118
height = 116
118 * 116 = 13,688

13,688 非常接近 opencv 给出的面积 (13,772),差异可能代表我的测量错误。 我手动计算了点的面积

Dot Area
width = 27
height = 6 
27*6 = 162

与opencv所说的(221)相差不远

OpenCV docs page on contourArea 中读到它说,对于具有自相交的轮廓,它会给出错误的结果。不太明白什么是自相交,我做了一个测试图像。

如您所见,我在左侧有一个矩形,中间有一个十字,另一个十字旋转了 45 度。 由于中心重叠,我希望十字架的面积略小于矩形面积的两倍。

Contour[0] Area: 1805, Length: 423, Colour: Red 
Contour[1] Area: 947, Length: 227, Colour: Green 
Contour[2] Area: 1825, Length: 415, Colour: Blue 

如您所见,两个十字的面积略小于矩形面积的两倍。正如预期的那样。

我对捕捉正方形的内部或在左侧的形状和圆点周围画一个框不感兴趣(尽管这会很有趣),这并不是我在这个问题中要问的具体内容。

所以我的问题: 为什么我的不规则形状的面积被严重低估了?

  1. 我是否使用了错误的功能?
  2. 我是否错误地使用了正确的功能?
  3. 我是否在 opencv 中发现了错误?
  4. 自相交是否具有我的测试中未证明的含义?

我从this tutorial复制了大部分代码

我已将我的代码精简为下面这个自包含的示例。

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']))

【问题讨论】:

    标签: python python-3.x opencv


    【解决方案1】:

    findContours 找到的等高线的内部部分应该是用 white 颜色填充的。

    • 不要在findContours 之前使用cv.Cannycv.blur 也不是必需的)。
    • 确保轮廓是白色而不是黑色。
      您可以使用 cv.thresholdcv.THRESH_BINARY_INV 选项来反转极性。
      自动阈值建议添加cv.THRESH_OTSU 选项。

    您可以将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)
    

    结果(顶部图像):

    Contour[0] Area: 13531, Length: 476, Colour: Red
    Contour[1] Area: 184, Length: 71, Colour: Green
    Contour[2] Area: 4321, Length: 1202, Colour: Blue
    

    这是完整的(更新的)代码:

    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')
    

    我在两个地方添加了cv.imshow 进行测试。

    【讨论】:

    • 啊,谢谢,标记为正确并 +1。你知道为什么它准确估计了实心黑点的面积吗?
    • 它在 Canny 过滤器之后标记white edgesRETR_EXTERNAL 选项仅查找外部轮廓,因此结果有意义。
    猜你喜欢
    • 2012-08-27
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2014-09-02
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多