list(contour)'的结果表示什么?
我想搞清楚“轮廓”是什么意思,以及当我们使用OpenCV中的cv.FindContours函数创建轮廓时,存储了什么值(我用的是OpenCV 2.3.1和Python)。我用了一张简单的图片来测试:
找到轮廓后,我在ipython中执行了以下命令:
In [8]: contour
Out[8]: <cv2.cv.cvseq at 0x90a31a0>
In [10]: list(contour)
Out[10]:
[(256, 190),
(255, 191),
(112, 191),
(255, 191),
(256, 190),
(257, 191),
(257, 190)]
第一条命令显示,轮廓是一个cvSeq对象。
我在图片上标记了这些点,得到了如下图像(红色标记的圆圈是这些点):
我不太明白这是什么意思。
所以我的问题是,第二条命令的结果中的值(也就是list(contour))表示什么?
编辑:以下是我使用的代码。
import cv
img = cv.LoadImage('simple.jpeg')
imgg = cv.LoadImage('simple.jpeg',cv.CV_LOAD_IMAGE_GRAYSCALE)
storage = cv.CreateMemStorage(0)
contours = cv.FindContours(imgg,storage,cv.CV_RETR_TREE,cv.CV_CHAIN_APPROX_SIMPLE,(0,0))
print list(contours)
for i in list(contours):
cv.Circle(img,i,5,(0,0,255),1)
cv.ShowImage('img',img)
cv.WaitKey(0)
2 个回答
我对list(contour)
的输出做了一些额外的工作,以便更好地理解轮廓,感谢数学咖啡的回答。
1) 我在测试图像时犯了个错误。我以为它是二值图像,实际上它是一个灰度图像,还包含其他颜色。(感谢数学咖啡)。所以我把图像改成了纯黑白图像,这样我就能得到一个轮廓,然后再次测试。这次,list(contour)
给出了4个值,当我把它们画在图像上时,正好是那个框的四个角。
当我们使用'cv.DrawContours'函数时,会画出连接这些顶点的线。所以我推测cv.FindContours存储的是轮廓的顶点位置,实际上它是一个多边形。
2) 为了再次测试,我拿了一张T形的图像。
对此,我期待得到一个包含8个值的列表,这8个值是T形的8个角。
`list(contour)`打印出的列表包含10个值。(多出的2个值可能是我绘制时的错误)
[(92, 58), (92, 108), (174, 108), (175, 109), (175, 239), (225, 239), (225, 109), (226, 108), (285, 108), (285, 58)]
这意味着cv.FindContours创建了一个cvseq对象。里面存储了我上面假设的值。
3) 上面的例子只找到了一个轮廓。那么在什么情况下会找到多个轮廓呢?我没有完全理解数学咖啡解释的多个链接序列的概念。为了测试这个,我拿了一张第三张图像。
现在cv.FindContours找到了三个轮廓。记住,每个轮廓都是一个框的4个角的列表。这三个列表存储在一个单一的cvseq对象中,而指针只指向第一个轮廓,也就是第一个框的顶点列表。因此,使用上面的代码,只画出了一个框的角。
要获取第二个顶点的列表,我们使用contour.h_next函数(感谢数学咖啡,我直到现在才知道它的功能)。现在它指向第二个框的轮廓。这样我们就可以遍历里面的所有列表。
所以我添加了一个简单的while循环,如下所示:
while contours:
print list(contours)
for i in list(contours):
cv.Circle(img,i,5,(0,0,255),3)
contours = contours.h_next()
我得到了三个列表,分别对应三个框的角:
[(196, 237), (196, 279), (357, 279), (357, 237)]
[(141, 136), (141, 201), (346, 201), (346, 136)]
[(33, 39), (33, 92), (206, 92), (206, 39)]
输出的图像:
所以你可以想象,如果是一个圆形,"会有很多顶点"的输出会是什么样子。
现在一切都简单了。我之前没能理解轮廓的值。这就是为什么会有这么多混乱。谢谢。
更新 - 1:
关于新cv2
模块中轮廓的更多细节可以在这里找到:轮廓 -1 : 入门
更新 - 2:
所有这些解释都是针对cv2.CHAIN_APPROX_SIMPLE的。但如果我们使用cv2.CHAIN_APPROX_NONE,则会得到轮廓上的所有点。这个文章中详细解释了这个问题,并提供了示例:轮廓 - 5 : 层次结构
好的,我看了你的图片,你确实得到了每个区域的顶点。花了我一段时间才搞明白,因为我用的是 cv2
接口,而不是 cv
的。
几点说明:
- 你的输入图片
simple.jpeg
除了0和255,还有很多其他的灰度值,这很可能是因为JPEG压缩造成的。 - 因此,你从
FindContours
得到了多个区域,都是因为不同的灰度级别。 cv.FindContours
返回的是多个相连的序列,你需要逐个遍历它们才能获取所有的轮廓。- 你在示例中得到的轮廓是边界上的一个。
为了演示,我们来画出所有的轮廓。
contours = cv.FindContours(imgg,storage,cv.CV_RETR_TREE,cv.CV_CHAIN_APPROX_SIMPLE,(0,0))
colours = [ (0,255,0,0), # green
(255,0,0,0), # blue
(255,255,0,0), # cyan
(0,255,255,0), # yellow
(255,0,255,0), # magenta
(0,0,255,0)] # red
i=0
while contours:
cv.DrawContours(img, contours, colours[i], colours[i], 0, thickness=-1)
i = (i+1) % len(colours)
contours = contours.h_next() # go to next contour
cv.ShowImage('img',img)
cv.WaitKey(0)
所以我们看到你在原问题中用 list(contours)
得到的第一个轮廓,就是方块底部那条小绿条,得到的点对应它的顶点。
在矩形边缘和角落周围出现这么多奇怪的小轮廓,可能是因为保存图片为JPEG时引入的压缩伪影,因为JPEG是有损的。如果你用无损格式保存你的方块(比如PNG或TIFF),你就只会得到一个由矩形角定义的轮廓。
总结经验:
cv.FindContours
确实给出了每个轮廓的“顶点”。- 你需要用
contours = contours.h_next()
来遍历每个轮廓。 - 如果保存为JPEG,记得要期待伪影!用TIFF/PNG或其他无损格式吧!