部分 IV
OpenCV 中的图像处理
21 OpenCV 中的轮廓
21.1 初识轮廓
目标
• 理解什么是轮廓
• 学习找轮廓,绘制轮廓等
• 函数:cv2.findContours(),cv2.drawContours()
21.1.1 什么是轮廓
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同、的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
• 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理、或者 Canny 边界检测。
• 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图、像的话,你应该将原始图像存储到其他变量中。
• 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住,、要找的物体应该是白色而背景应该是黑色。
让我们看看如何在一个二值图像中查找轮廓:
函数 cv2.findContours() 有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个 Python列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。
注意:我们后边会对第二和第三个参数,以及层次结构进行详细介绍。在那之前,例子中使用的参数值对所有图像都是适用的。
21.1.2 怎样绘制轮廓
函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。
在一幅图像上绘制所有的轮廓:
importnumpy as np
importcv2
im = cv2.imread('test.jpg')
imgray =cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
image, contours, hierarchy =cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
绘制独立轮廓,如第四个轮廓:
img = cv2.drawContour(img, contours, -1, (0,255,0), 3)
但是大多数时候,下面的方法更有用:
img = cv2.drawContours(img, contours, 3, (0,255,0), 3)
注意:最后这两种方法结果是一样的,但是后边的知识会告诉你最后一种方法更有用。
21.1.3 轮廓的近似方法
这是函数 cv2.findCountours() 的第三个参数。它到底代表什么意思呢?
上边我们已经提到轮廓是一个形状具有相同灰度值的边界。它会存贮形状边界上所有的 (x,y) 坐标。但是需要将所有的这些边界点都存储吗?这就是这个参数要告诉函数 cv2.findContours 的。
这个参数如果被设置为 cv2.CHAIN_APPROX_NONE,所有的边界点都会被存储。但是我们真的需要这么多点吗?例如,当我们找的边界是一条直线时。你用需要直线上所有的点来表示直线吗?不是的,我们只需要这条直线的两个端点而已。这就是 cv2.CHAIN_APPROX_SIMPLE 要做的。它会将轮廓上的冗余点都去掉,压缩轮廓,从而节省内存开支。我们用下图中的矩形来演示这个技术。在轮廓列表中的每一个坐标上画一个蓝色圆圈。第一个图显示使用 cv2.CHAIN_APPROX_NONE 的效果,一共 734 个点。第二个图是使用 cv2.CHAIN_APPROX_SIMPLE 的结果,只有 4 个点。看到他的威力了吧!
21.2 轮廓特征
目标
• 查找轮廓的不同特征,例如面积,周长,重心,边界框等。
• 你会学到很多轮廓相关函数
21.2.1 矩
图像的矩可以帮助我们计算图像的质心,面积等。详细信息请查看维基百科Image Moments。
函数 cv2.moments() 会将计算得到的矩以一个字典的形式返回。如下:
importcv2importnumpy as np
img= cv2.imread('star.jpg',0)
ret,thresh= cv2.threshold(img,127,255,0)
contours,hierarchy= cv2.findContours(thresh, 1, 2)
cnt=contours[0]
M=cv2.moments(cnt)print M
根据这些矩的值,我们可以计算出对象的重心:
和
. 。
#This can be done as follows:
cx = int(M['m10']/M['m00'])
cy= int(M['m01']/M['m00'])
21.2.2 轮廓面积
轮廓的面积可以使用函数 cv2.contourArea() 计算得到,也可以使用矩(0 阶矩),M['m00']。
area = cv2.contourArea(cnt)
21.2.3 轮廓周长
也被称为弧长。可以使用函数 cv2.arcLength() 计算得到。这个函数的第二参数可以用来指定对象的形状是闭合的(True),还是打开的(一条曲线)。
perimeter = cv2.arcLength(cnt,True)
21.2.4 轮廓近似
将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目由我们设定的准确度来决定。使用的Douglas-Peucker算法,你可以到维基百科获得更多此算法的细节。
为了帮助理解,假设我们要在一幅图像中查找一个矩形,但是由于图像的种种原因,我们不能得到一个完美的矩形,而是一个“坏形状”(如下图所示)。
现在你就可以使用这个函数来近似这个形状()了。这个函数的第二个参数叫epsilon,它是从原始轮廓到近似轮廓的最大距离。它是一个准确度参数。选择一个好的 epsilon 对于得到满意结果非常重要。
epsilon = 0.1*cv2.arcLength(cnt,True)
approx= cv2.approx