查找 OpenCV 轮廓的面积

Find Area of a OpenCV Contour

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

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

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

问题: 当等高线不闭合时,力矩是错误的。

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

我是做什么的:

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

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

下面是绘制了轮廓的图像示例。在这里,该区域被确定为 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']))

我的问题是,正如@HugoRune 所指出的,countour 中存在差距。解决办法是缩小差距。

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

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

处理过类似的问题后,另一种解决方案(可以说更简单且开销更少)是使用形态学开函数,它先执行腐蚀,然后执行膨胀。如果您首先将其转换为二值图像,执行开操作,然后执行 Canny 检测,那应该做同样的事情,但不必迭代过滤器。您唯一需要做的就是尝试几次内核大小,以确定合适的大小而不会丢失太多细节。我发现这是确保轮廓闭合的相当可靠的方法。

Morphological operations documentation

另一种方法是使用轮廓点来查找区域。此处 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);

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