OpenCV 中的轮廓
- 理解什么是轮廓
- 学习找轮廓,绘制轮廓等
- 函数:cv2.findContours(),cv2.drawContours()
一、查找轮廓
-
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用
- 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测。
- 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图像的话,你应该将原始图像存储到其他变量中。
- 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住,要找的物体应该是白色而背景应该是黑色。
-
使用cv2.findContours()在二值图像中查找轮廓:
- 函数 cv2.findContours() 有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个 Python列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。
二、绘制轮廓
函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。
在一幅图像上绘制所有的轮廓:
import numpy as np
import cv2
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
img = cv2.imread('opencv.jpg')
imgray = cv2.cvtColor(img,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.drawContours(img, contours, -1, (0,255,0), 3)
cv_show(img,'img')
#但是大多数时候,下面的方法更有用:
img = cv2.drawContours(img, contours, 3, (0,255,0), 3)
cv_show(img,'img')
轮廓的近似方法
这是函数 cv2.findCountours() 的第三个参数。它到底代表什么意思呢?
上边我们已经提到轮廓是一个形状具有相同灰度值的边界。它会存贮形状边界上所有的 (x,y) 坐标。但是需要将所有的这些边界点都存储吗?这就是这个参数要告诉函数 cv2.findContours 的。
这个参数如果被设置为 cv2.CHAIN_APPROX_NONE,所有的边界点都会被存储。但是我们真的需要这么多点吗?例如,当我们找的边界是一条直线时。你用需要直线上所有的点来表示直线吗?不是的,我们只需要这条直线的两个端点而已。这就是 cv2.CHAIN_APPROX_SIMPLE 要做的。它会将轮廓上的冗余点都去掉,压缩轮廓,从而节省内存开支。
我们用下图中的矩形来演示这个技术。在轮廓列表中的每一个坐标上画一个蓝色圆圈。第一个图显示使用cv2.CHAIN_APPROX_NONE 的效果,一共 734 个点。第二个图是使用 cv2.CHAIN_APPROX_SIMPLE 的结果,只有 4 个点。看到他的威力了吧!
三、轮廓特征
- 查找轮廓的不同特征,例如面积,周长,重心,边界框等。
- 轮廓相关函数
(1)图像矩
图像的矩可以帮助我们计算图像的质心,面积等。函数 cv2.moments() 会将计算得到的矩以一个字典的形式返回。
根据这些矩的值,我们可以计算出对象的重心:
import cv2
import numpy as np
img = cv2.imread('tower.jpg',0)
ret,thresh = cv2.threshold(img,127,255,0)
image, contours,hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)
print(M)
{'m00': 2.0, 'm10': 742.0, 'm01': 1502.0, 'm20': 275282.3333333333, 'm11': 557242.0, 'm02': 1128002.3333333333, 'm30': 102129993.0, 'm21': 206737032.33333334, 'm12': 418488865.6666667, 'm03': 847130253.0, 'mu20': 0.3333333333139308, 'mu11': 0.0, 'mu02': 0.3333333332557231, 'mu30': 1.4901161193847656e-08, 'mu21': 2.450542524456978e-08, 'mu12': 4.866160452365875e-08, 'mu03': 1.1920928955078125e-07, 'nu20': 0.0833333333284827, 'nu11': 0.0, 'nu02': 0.08333333331393078, 'nu30': 2.634178031930877e-09, 'nu21': 4.331988091573825e-09, 'nu12': 8.60223763552427e-09, 'nu03': 2.1073424255447017e-08}
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx,cy)
371 751
(2)轮廓面积
- 使用函数 cv2.contourArea() 计算
- 使用(0 阶矩),M[‘m0