OpenCV cv2.moments 将所有矩归零

lin*_*llo 4 python geometry opencv image-processing computer-vision

由于某种我无法理解的原因, open cv 函数cv2.moments返回一个字典,其中我提供的轮廓的值全部为零。这是一个 MWE:

contour = [[[271, 67]],
           [[274, 67]],
           [[275, 68]],
           [[278, 68]],
           [[279, 69]],
           [[283, 69]],
           [[284, 70]],
           [[287, 70]],
           [[288, 71]],
           [[291, 71]],
           [[292, 72]],
           [[295, 72]],
           [[292, 72]],
           [[291, 71]],
           [[288, 71]],
           [[287, 70]],
           [[284, 70]],
           [[283, 69]],
           [[279, 69]],
           [[278, 68]],
           [[275, 68]],
           [[274, 67]]]

contour = np.asarray(contour)
moments = cv2.moments(contour)
Run Code Online (Sandbox Code Playgroud)

结果:

print(moments)

{'m00': 0.0, 'm10': 0.0, 'm01': 0.0, 'm20': 0.0, 'm11': 0.0, 'm02': 0.0, 'm30': 0.0, 'm21': 0.0, 'm12': 0.0, 'm03': 0.0, 'mu20': 0.0, 'mu11': 0.0, 'mu02': 0.0, 'mu30': 0.0, 'mu21': 0.0, 'mu12': 0.0, 'mu03': 0.0, 'nu20': 0.0, 'nu11': 0.0, 'nu02': 0.0, 'nu30': 0.0, 'nu21': 0.0, 'nu12': 0.0, 'nu03': 0.0}
Run Code Online (Sandbox Code Playgroud)

这种行为有何意义?我相信这是因为轮廓是开放的,但我不确定。是否有一种标准方法可以通过预先检查轮廓是打开还是关闭来消除这种行为?

Chr*_*itz 5

轮廓点

这些是轮廓的点。GIF 不会来回循环,只会向前播放。当该点“再次爬升”时,这些点不再是相同的点,而是与之前的点重合的新点。检查轮廓中的点,您就会看到它。

你的轮廓不是“开放的”。这不是问题。在 OpenCV 中,所有轮廓都被认为是闭合的。它们也不必很密集(点之间的距离为 1 像素)。

问题是cv.moments()有一个错误并且 OpenCV 轮廓很奇怪。

并且您的轮廓没有严格的数学“多边形”意义上的面积,因为它是根据 1 像素线计算的。

就是 中的错误cv.moments()。它计算这些力矩,就好像轮廓是一个实际的多边形一样。它应该m00为此轮廓返回一个非零区域 (,因为该轮廓描述了一个非零区域连通分量。

在 OpenCV 中,轮廓描述必须使用 1 像素笔划绘制的多边形,以重现该连接组件的边界像素。轮廓在边界像素上延伸,穿过它们的中心。

这意味着,如果您有一个 2 x 2 像素的矩形,您将在这些边界像素获得轮廓点,例如 (1,1)、(2,1)、(2,2)、(1,2)。它的面积仅为 1(作为数学多边形),而它应该是 4(在 OpenCV 的轮廓概念中)。对于一条普通的线,同样的事情,但现在你的面积为零。

你的多边形的面积确实为零。你只是看不到它,因为线不直。

OpenCV 可以将轮廓定义为零宽度线,限制连接组件的外部像素,即完全沿着像素边缘延伸。那需要:

  1. OpenCV 中的绘图要求不要像它们那样被破坏和忽视(我很直率,但这是事实)
  2. 允许非整数坐标的轮廓约定

欢迎在 OpenCV 的 github 页面上提交问题。实施cv.moments()需要修复。该修复可能应该是调用的一个附加标志,用于区分“轮廓”与正确的多边形。这样,数学上正确的行为就不会丢失,而是变成可选的。每个人都在轮廓上调用该函数,因此这应该是默认值。


另一个答案“有效”,因为现在是在从轮廓绘制的掩模cv.moments()上调用而不是在轮廓本身上调用。您可以跳过整个轮廓计算。也许可以与其他人一起工作。connectedComponents()

其他答案的解释是错误的,但它并不建议修复,只是跳过这种情况。