上一篇已经讲解了很多算子用来检测边缘,其中用得最多的是canny边缘检测。只有边缘还不够,有很多时候我们还需要获得图片上的某物体轮廓。
轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
发现轮廓
Opencv提供了一个函数findContours()用于发现轮廓,它有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。
轮廓的近似方法
轮廓是一个形状具有相同灰度值的边界,它会存储形状边界上所有的(x,y)坐标。实际上我们不需要所有的点,当需要直线时,找到两个端点即可。CHAIN_APPROX_SIMPLE可以实现。它会将轮廓上的冗余点去掉,压缩轮廓,从而节省内存开支。
下面用矩阵来演示,在轮廓列表中的每一个坐标上画一个蓝色圆圈。第一个显示使用CHAIN_APPROX_NONE的效果,一共734个点,第二个图是使用CHAIN_APPROX_SIMPLE的结果,只有4个点。
# coding = utf-8
import numpy as np
import cv2 as cv
img = cv.imread('1024.jpg')
imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
image, contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
findContours()的返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。最常用的是第二个返回值。
轮廓(第二个返回值)是一个Python列表,其中储存这图像中所有轮廓。每一个轮廓都是一个Numpy数组,包含对象边界点(x,y)的坐标。
绘制轮廓
Opencv提供了一个函数drawContours()用于绘制轮廓。
image, contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
img2 = np.copy(img)
cv.drawContours(img2, [cnt], 0, (0, 0, 255), 2)
轮廓特征
1、轮廓面积
area = cv.contourArea(cnt)
轮廓特征计算的结果并不等同于像素点的个数,而是根据几何方法算出来的,所以有小数。
2、轮廓周长
perimeter = cv.arcLength(cnt, True)
参数2表示轮廓是否封闭
3、外接矩形
形状的外接矩形有两种,如下图,绿色的叫外接矩形,表示不考虑旋转并且能包含整个轮廓的矩形。蓝色的叫最小外接矩,考虑了旋转。
x, y, w, h = cv.boundingRect(cnt) # 外接矩形
cv.rectangle(img2, (x, y), (x + w, y + h), (0, 255, 0), 2)
rect = cv.minAreaRect(cnt) # 最小外接矩形
box = np.int0(cv.boxPoints(rect)) # 矩形的四个角点取整
cv.drawContours(img2, [box], 0, (255, 0, 0), 2)
其中np.int0(x)是把x取整的操作,比如377.93就会变成377,也可以用x.astype(np.int)
4、最小外接圆
外接圆跟外接矩形一样,找到一个能包围物体的最小圆:
(x, y), radius = cv.minEnclosingCircle(cnt)
(x, y, radius) = np.int0((x, y, radius)) # 圆心和半径取整
cv.circle(img2, (x, y), radius, (0, 0, 255), 2)
5、外接椭圆
我们可以用得到的轮廓拟合出一个椭圆:
ellipse = cv.fitEllipse(cnt)
cv.ellipse(img2, ellipse, (255, 255, 0), 2)
6、判断点和轮廓关系
result = cv.pointPolygonTest(biggest, (w,h), False)
第一个参数是某一轮廓。第二个参数是像素点坐标。第三个参数如果为True则输出该像素点到轮廓最近距离;如果为False,则输出为正表示在轮廓内,0为轮廓上,负为轮廓外。