title | date | tags |
---|---|---|
寻找图像中物体的轮廓——find countours in image |
2017-01-13 05:38:42 -0800 |
countours、轮廓检测 |
前面我们学习了图像的梯度以及图像中的物体的边缘寻找方法,现在我们要进一步使用这些方法了,我们现在想获得某个物体的轮廓。以前我听过一些计算机视觉的网络课程,课上的老师提到过动物的视觉系统在辨认物体的时候总是先从简单的轮廓之类的信息开始的,而不是整体的图片,所以我们如果想提取图像中的物体,至少我们得先知道这个物体的大致轮廓吧,然后才是这些轮廓的位置,最后我们才能将其提取出来。
这一步骤不难解释,我们前面看见了Canny方法的厉害,所以我们现在需要一个类似我们前面的Canny边缘检测出来的图像的二值化图像,这样对于我们的检测来说会比较方便。当然,你也可以使用简单阈值或者是自适应式的阈值方法来获取这些二值图像。
在opencv中我们使用findContours函数来获取图像中的轮廓信息,名字倒是挺通俗易懂的,在我们解释这个函数的返回值以及函数的参数之前,我们还是来看一下怎样使用的吧!
import cv2
import numpy
imagePath = './images/coins.jpg'
img = cv2.imread(imagePath, 0)
img = cv2.GaussianBlur(img, (5, 5), 0)
edge = cv2.Canny(img.copy(), 30, 120)
#
# 下面这一句是寻找图像中的轮廓
#
cnts, heri = cv2.findContours(edge.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#
# 在img图像上绘制我们找到的轮廓
#
cv2.drawContours(img, cnts, 2, 50, 2)
allImage = numpy.hstack([
img, edge
])
cv2.imshow( 'All image', allImage)
cv2.waitKey(0) & 0XFF
cv2.destroyAllWindows()
首先说一下,我的图片是一张包含有多个硬币的图片,所以上面的代码最终会显示在每一个硬币的外围都绘制得有边界,还挺准的。
下面来解释一下findContours函数的参数:
-
edge 我们需要寻找轮廓的二值图像,需要注意的是,这个函数运行之后会对原来的edge图像有所破坏,所以如果你在后期还需要使用这一章图片的话,那么你最好像我一样使用copy函数(这是个numpy的函数)
-
mode 定义寻找轮廓的模式,这个模式可以是只寻找最外层的轮廓或者是全部都寻找,在上面的程序中我们传递进去的是cv2.RETR_EXTERNAL,表示我们需要寻找最外层的轮廓,因为我们的确是这么要求的,另外还有寻找所有的轮廓的 cv2.RETR_LIST和cv2.RETR_TREE、cv2.RETR_COMP
-
method 这个方法表示我们获得了相应的轮廓之后,我们是用什么方法来表示这些轮廓,有时我们只需要大约的轮廓就可以绘制出整体的轮廓了,这个时候我们传递进去cv2.CHAIN_APPROX_SIMPLE,如果我们想获取所有的轮廓上的位置,那么我们可以传递 cv2.CHAIN_APPROX_NONE,不过有的时候这是没有必要的,因为说实话,我们寻找一个大概的轮廓对我们来说已经足够了,毕竟计算机视觉本身就是模糊的,太精确的东西会很浪费资源和时间。
这个函数返回一个元组包含两个元素,第一个元素就是我们获取到的轮廓的坐标的列表,另外一个是这些轮廓的层级关系(这个我就解释不清楚了,反正就是阐述了这些获得的轮廓之间的关系的一个数据,比如包含与被包含的关系)。
接下来我们需要使用到这些我们获得的轮廓数据,首先我们最应该想做的事情应该是绘制出这些轮廓,看一下对还是不对,所以我们就调用了drawContours这个函数,这个函数的参数如下:
-
img 也可以说是画布,我们将在该图片上绘制这些轮廓
-
cnts 包含轮廓的列表
-
index 我们想要绘制轮廓列表中的那一个轮廓,那就传递那一个轮廓的下标值,如果向全部绘制出来,那么就传递-1
-
color 绘制的颜色,需要注意的是我们有可能在灰度图像或者彩色图像上绘制这些轮廓,所以请自己更改相关的代码。
-
thickness 线宽
其实opencv还有估算出最小多边形之类的函数,只不过在2.x版本上可能用不了,在2.x上用的比较多的是估算出最小包含矩形的函数,来看一下吧!
#
# 估算出最小包含矩形并绘制出来
#
for i, c in enumerate(cnts):
(x, y ,w, h) = cv2.boundingRect(c)
cv2.rectangle(img, (x, y), (x+w, y+h), 50, 2)
#
# 估算出最小包含圆形并绘制出来
#
for i, c in enumerate(cnts):
((centerX, centerY), radius) = cv2.minEnclosingCircle(c)
cv2.circle(img, (int(centerX), int(centerY)), int(radius), 200, 4)
需要注意的是我们使用minEnclosingCircle得到的返回值的数据类型都是float,需要将其转换成int类型才可以正确显示。
这一篇还挺长的,不过总体来说,我们所讲解的东西却不是很多,我们主要介绍了如何使用opencv来获取图像中的轮廓,其中轮廓还可以以不同的方式来提取,另外就是将轮廓绘制出来。最后我们还介绍了两个小小的技巧——估算某一个轮廓的最小包含矩形和圆形。
下一篇我们将学习如何退场。