'list(contour)' 的结果表示什么?(这是一个关于IT技术的提问)

4

我只是想了解什么是轮廓(contour),以及使用cv.FindContours函数在OpenCV(我正在使用OpenCV 2.3.1和Python)中创建轮廓时存储的值是什么。我使用以下简单图像进行测试:

enter image description here

在找到轮廓之后,我在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对象。

我在图像上标记了这些点,得到了以下图像(红色圆圈是这些点):

enter image description here

我不明白这意味着什么。

所以我的问题是,第二个命令的结果中的值(即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)

你正在使用什么 方法?是 CV_CHAIN_APPROX_SIMPLE 吗?(在这种情况下,文档说“一个正立的矩形轮廓由4个点编码”,但似乎并不是这样...) - mathematical.coffee
这是我使用的命令: contours = cv.FindContours(img, storage, cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE, (0,0)) - Abid Rahman K
嗯,当我做你所做的事情时,我会得到四个角落。您能发布完整的代码以重现此问题吗?z=np.zeros((100,200)).astype('uint8'); cv2.rectangle(z,(20,30),(60,80),255,-1); cs=cv.FindContours(cv.fromarray(z),cv.CreateMemStorage(),mode=cv.CV_RETR_TREE,method=cv.CV_CHAIN_APPROX_SIMPLE); list(cs) 返回预期的 [(20, 30), (20, 80), (60, 80), (60, 30)] - mathematical.coffee
已经编辑了带有代码的问题。 - Abid Rahman K
2个回答

4
好的,我看了你的图片,并且你确实得到了每个区域的顶点。我花了一段时间来研究它,因为我使用的是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格式时引入了压缩伪影。如果使用无损格式(如PNG或TIFF)保存正方形,则只会得到由矩形的四个角定义的一个轮廓。

总结经验:

  • cv.FindContours确实提供了每个轮廓的“顶点”
  • 您需要contours = contours.h_next()来迭代遍历每个轮廓
  • 如果保存为JPEG,则可能会出现伪影!请改用TIFF/PNG/其他无损格式!

你检查过你的代码了吗?while循环陷入了无限循环。我将我的图像转换为png格式,并使用list(contours)再次进行了检查。现在它清楚地给出了那个正方形的4个角落。很好。那么cv.DrawContours到底是做什么的呢?用直线连接所有这些角落吗? - Abid Rahman K
我检查了我的代码,这就是我生成图片的方式 - 对我有效。cv.DrawContours .. 用于绘制轮廓。thickness=-1 表示填充轮廓(默认只绘制边框)。 - mathematical.coffee
当我移除了while循环时,它可以正常工作。否则会进入无限循环。 - Abid Rahman K

4

我对list(contour)的输出做了更多的工作,以便通过mathematical.coffee提供的答案更好地理解轮廓。

1)我在我的测试图像上犯了一个错误。我原以为它是二进制图像,但实际上它是一个带有其他颜色的灰度图像。(感谢mathematical.coffee)。因此,我将图像更改为纯黑白图像,以便只得到一个轮廓并再次进行测试。这次list(contour)给出了4个值,当在图像上绘制时,它们是该框的四个角。

New output of image in question

因此,当我们使用'cv.DrawContours'函数时,会画出连接所有这些顶点的线条。因此,我假设cv.FindContours存储的是轮廓的顶点位置,而这实际上是一个多边形。

2) 为了再次测试,我选择了另一张图像,这是一个T形。

test-image-2

对于这个,我期望得到8个值的列表,它们是T的8个角。

Output of image 2

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) 上面的例子只找到了一个轮廓。如果找到多个轮廓,那么情况将是什么样子?我没有完全理解数学.coffee所解释的多个链接序列的概念。因此,我选择了第三张图片进行测试。

test image 3

现在cv.FindContours发现了三个轮廓。请记住,每个轮廓都是由盒子的4个角组成的列表。这三个列表存储在一个单独的cvseq对象中,并且指针仅指向第一个轮廓,即仅指向第一个盒子的顶点列表。因此,使用上述代码只绘制了一个盒子的角落。

要获取第二个顶点列表,我们使用contour.h_next函数(感谢mathematical.coffee,我直到现在才知道它的功能)。现在它指向第二个盒子的轮廓。因此,我们遍历其中的所有列表。

因此,我添加了一个简单的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)]

下面是输出的图片:

图像3的输出

因此,你可以预期一个拥有大量顶点的圆形的输出。

现在一切都很简单。我无法掌握轮廓值。这就是为什么出现了这种混乱。谢谢。

更新-1:

有关新cv2模块中轮廓的更多详细信息,请参见此处:轮廓-1:入门

更新-2:

所有这些解释都是针对cv2.CHAIN_APPROX_SIMPLE的。但是,如果我们使用cv2.CHAIN_APPROX_NONE,我们将获得轮廓上的所有点。这在本文中通过示例详细说明:轮廓-5:层次结构


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接