【问题标题】:Find Area of a OpenCV Contour查找 OpenCV 轮廓的区域
【发布时间】:2015-06-24 09:47:43
【问题描述】:

在最近的一组图像中,我的 OpenCV 代码停止寻找轮廓的正确区域。当轮廓未闭合时,这似乎会发生。我试图确保轮廓关闭无济于事。

编辑:问题是轮廓中有间隙。

背景: 我在通道中有一系列胶囊的图像,我想测量形状的面积以及时刻的质心。

问题: 当轮廓不闭合时,矩是错误的。

编辑:当我有间隙时,轮廓不是整个形状,因此区域不正确。

我的工作:

  • 读取图像-> img =cv2.imread(fileName,0)
  • 应用 Canny 过滤器 -> 边缘 = cv2.Canny(img,lowerThreshold,lowerThreshold*2)
  • 查找轮廓 -> 轮廓,层次结构 = cv2.findContours(edges,cv2.cv.CV_RETR_LIST,cv2.cv.CV_CHAIN_APPROX_NONE)
  • 找到最长的轮廓
  • 确保轮廓闭合
  • 寻找时刻 -> cv2.moments(cnt)

可以在here找到一个带有测试图像的工作示例。

有一个关于关闭轮廓的question,但这些建议都没有奏效。使用 cv2.approxPolyDP 不会改变结果,尽管它应该返回一个闭合的轮廓。添加轮廓的第一个点作为最后一个点,以使其闭合,也不能解决问题。

下面是带有轮廓的图像示例。在这里,该区域被确定为 85,而在几乎相同的图像中,它是 8660,这是它应该的。

任何建议都会被采纳。

代码:

img =cv2.imread(fileName,0)
edges = cv2.Canny(img,lowerThreshold,lowerThreshold*2)
contours, hierarchy = cv2.findContours(edges,cv2.cv.CV_RETR_LIST,cv2.cv.CV_CHAIN_APPROX_NONE) #cv2.cv.CV_CHAIN_APPROX_NONE or cv2.cv.CV_CHAIN_APPROX_SIMPLE

#Select longest contour as this should be the capsule
lengthC=0
ID=-1
idCounter=-1
for x in contours:
    idCounter=idCounter+1 
    if len(x) > lengthC:
        lengthC=len(x)
        ID=idCounter

if ID != -1:
    cnt = contours[ID]
    cntFull=cnt.copy()

    #approximate the contour, where epsilon is the distance to 
    #the original contour
    cnt = cv2.approxPolyDP(cnt, epsilon=1, closed=True)

    #add the first point as the last point, to ensure it is closed
    lenCnt=len(cnt)
    cnt= np.append(cnt, [[cnt[0][0][0], cnt[0][0][1]]]) 
    cnt=np.reshape(cnt, (lenCnt+1,1, 2))

    lenCntFull=len(cntFull)
    cntFull= np.append(cntFull, [[cntFull[0][0][0], cntFull[0][0][1]]]) 
    cntFull=np.reshape(cntFull, (lenCntFull+1,1, 2))

    #find the moments
    M = cv2.moments(cnt)
    MFull = cv2.moments(cntFull)
    print('Area = %.2f \t Area of full contour= %.2f' %(M['m00'], MFull['m00']))

【问题讨论】:

  • 好:您搜索了上一个问题并找到了相关内容,并在您的问题中提到了这一点。不好:你只是说这些建议不起作用。为什么他们没有工作?你试过什么?现在,我给您的答案与上一个问题完全相同:确保您的轮廓围绕整个对象闭合,例如通过膨胀或凸包。如果边界中有间隙,则该区域将始终是错误的。另外,请附上一张解压处理图像(即画出你的轮廓),以吸引最有潜力的回答者。
  • 解决一些误解:findContours 将始终返回闭合轮廓。 ApproxPoly 或在末尾添加第一个点不会改变这一点。你的问题不是轮廓没有闭合,你的问题是轮廓闭合在错误的区域,即如果你将一个精巧的边缘图像传递给包含间隙的 findContours,找到的轮廓将被关闭,但它包含的区域将只是边缘本身,而不是内部。对于初学者,我会避免精明并在 findContours 之前使用简单的阈值。
  • 重读上一个问题,我觉得这个问题有些误导。正如我所说,我很确定 findContours 返回一个封闭的轮廓。如果你放大你的图像,我想你会发现在错误的情况下,轮廓沿着对象的边界运行了两次,一次在外面,一次在里面,这样它就包含了你的对象的整个边界,但不是它的内部。如果精巧的边缘图像仅包含单个间隙,则对象上方的凸包将解决此问题。 Canny 边缘图像的扩张将关闭任意数量的小间隙。
  • @NegativeProbability 你可以将单个点绘制为单个像素而不是小圆圈吗?很难看出是否有缝隙。如果您改用“drawContours”(填充)函数,您将看到 openCV 如何解释轮廓,因此您可能会了解面积计算失败的原因。
  • @Micka 这里link HugoRune 是正确的,问题是轮廓中有间隙。我将修改问题以明确这一点。

标签: python opencv image-processing


【解决方案1】:

正如@HugoRune 指出的那样,我的问题是计数存在差距。解决办法是缩小差距。

我发现很难找到缩小间隙的通用方法,因此我反复更改 Canny 过滤器的阈值并执行 morphological closing 直到找到闭合轮廓。

对于那些遇到同样问题的人,有几个很好的答案如何关闭轮廓,例如thisthis

【讨论】:

    【解决方案2】:

    在处理了类似的问题后,另一种解决方案(可以说是更简单且开销更少)是使用形态开放功能,该功能先执行腐蚀,然后执行膨胀。如果你先把它变成二值图像,执行开运算,然后做 Canny 检测,应该做同样的事情,但不必用过滤器迭代。您唯一需要做的就是多次使用内核大小来确定合适的大小而不会丢失太多细节。我发现这是确保轮廓闭合的一种相当可靠的方法。

    Morphological operations documentation

    【讨论】:

      【解决方案3】:

      另一种方法是使用轮廓点来查找区域。这里 nContours 以前是通过 cvFindContours() 找到的。我在这里使用了 MFC CArray。您也可以使用 std::vector 。

      /////////////////////////////////////

      CvSeq* MasterContour = NULL;
      if (nContours > 1)
      {
          // Find the biggest contour
          for (int i = 0; i < nContours; i++)
          {
              CvRect rect = cvBoundingRect(m_contour, 1);
              if (rect.width > rectMax.width)
                  MasterContour = m_contour;
              if (m_contour->h_next != 0)
                  m_contour = m_contour->h_next;
              else
                  break;
          }
      }
      else
          MasterContour = m_contour;
      
      arOuterContourPoints.RemoveAll();
      CArray<CPoint, CPoint> arOuterTrackerPoints;
      for (int i = 0; i < MasterContour->total; i++)
      {       
          CvPoint *pPt; 
          pPt = (CvPoint *)cvGetSeqElem(MasterContour, i);
          arOuterContourPoints.Add(CPoint(pPt->x, pPt->y));
      }
      int nOuterArea = 0;
      for (int i = 0; i < arOuterContourPoints.GetSize(); i++)
      {
          if (i == (arOuterContourPoints.GetSize() - 1))
              nOuterArea += (arOuterContourPoints[i].x * arOuterContourPoints[0].y - arOuterContourPoints[0].x * arOuterContourPoints[i].y);
          else
              nOuterArea += (arOuterContourPoints[i].x * arOuterContourPoints[i+1].y - m_arOuterContourPoints[i+1].x * m_arOuterContourPoints[i].y);
      }
      nOuterAreaPix = abs(nOuterArea / 2.0);
      

      /////////////////////////////////////// /////////////

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-07
        • 1970-01-01
        • 1970-01-01
        • 2012-01-12
        • 1970-01-01
        • 2012-11-06
        相关资源
        最近更新 更多