查找 OpenCV 轮廓的面积
Find Area of a OpenCV Contour
在最近的一组图像中,我的 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 不会改变结果,尽管它应该 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 检测,那应该做同样的事情,但不必迭代过滤器。您唯一需要做的就是尝试几次内核大小,以确定合适的大小而不会丢失太多细节。我发现这是确保轮廓闭合的相当可靠的方法。
另一种方法是使用轮廓点来查找区域。此处 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);
/////////////////////////////////////////// ///////////////
在最近的一组图像中,我的 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 不会改变结果,尽管它应该 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 检测,那应该做同样的事情,但不必迭代过滤器。您唯一需要做的就是尝试几次内核大小,以确定合适的大小而不会丢失太多细节。我发现这是确保轮廓闭合的相当可靠的方法。
另一种方法是使用轮廓点来查找区域。此处 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);
/////////////////////////////////////////// ///////////////