首页 > 解决方案 > cv2.findContours + cv2.contourArea 意外结果

问题描述

概括

示例图像

cv.findContours+此图像的cv2.contourArea回报总和。0.0

重现步骤

# Create sample image
import numpy as np
a = np.array([
   [0, 0],
   [0, 255]
])

img = np.vstack([np.hstack([a]*100)]*100)
# Show image
from PIL import Image
Image.fromarray(img).show()

示例图像

import cv2
cv2.__version__
>>> 4.3.0
cnts, hierachy = cv2.findContours(img.astype(np.uint8), 
                                  mode=cv2.RETR_LIST, 
                                  method=cv2.CHAIN_APPROX_NONE)
len(cnts)
>>> 10000
sum(cv2.contourArea(cnt) for cnt in cnts)
>>> 0.0

实时调频

cnts[-1]
>>> array([[[1,1]]], dtype=int32)

轮廓是根据文档使用边界遵循算法[1] 计算的,这导致轮廓描述轮廓的内部多边形。意想不到的部分是,计算面积需要轮廓的外部或封闭多边形。

上面的极端情况是有 10000 个非常小的对象,每个对象的面积为 1px。1px 的内部多边形的面积为 0。所有轮廓面积的总和仍为 0,而预期为 10000。

github issue上有一个讨论,这实际上是所需的行为,1px 的对象应该有区域 0。对于缺陷分析,这是不可接受的。

在一般情况下如何找到一致的面积计算?

废弃的解决方案

  1. 像素值求和:对整个图像中的渲染像素值求和将给出非常准确的总面积,但不是单个组件的面积。将轮廓渲染成单独的图像并在那里求和具有低性能。

  2. 在 findContours 之前扩张轮廓:扩张会弄乱细粒度的轮廓边界。此外,必须分别绘制和扩展每个轮廓,以免将多个轮廓聚集成一个。这再次提供了低性能。

  3. 改用 connectedCompontens:connectedComponentsWithStats 只会返回组件的像素数,这是一个很好的面积近似值,但不如计算多边形面积准确。例如,尝试用很多非常小的三角形/圆形进行试验。

  4. 使用 skimage.measure.find_contours:此函数将返回轮廓的行进正方形近似值。对于从 1px 对象构造的多边形,这种近似方法将产生 2px 的面积。

到目前为止,我们已经进行了数十年的图像处理,我确信必须存在一个干净、高性能的解决方案来返回一致的正确多边形 + 面积计算。我期待着您的建议。


[1] 铃木聪等。边界跟随数字化二值图像的拓扑结构分析 计算机视觉、图形和图像处理,30(1):32–46, 1985。

标签: pythonopencv

解决方案


推荐阅读