从图像边缘到物体轮廓

上一篇已经讲解了很多算子用来检测边缘,其中用得最多的是canny边缘检测。只有边缘还不够,有很多时候我们还需要获得图片上的某物体轮廓。
轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。

发现轮廓

Opencv提供了一个函数findContours()用于发现轮廓,它有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。

轮廓的近似方法
轮廓是一个形状具有相同灰度值的边界,它会存储形状边界上所有的(x,y)坐标。实际上我们不需要所有的点,当需要直线时,找到两个端点即可。CHAIN_APPROX_SIMPLE可以实现。它会将轮廓上的冗余点去掉,压缩轮廓,从而节省内存开支。
下面用矩阵来演示,在轮廓列表中的每一个坐标上画一个蓝色圆圈。第一个显示使用CHAIN_APPROX_NONE的效果,一共734个点,第二个图是使用CHAIN_APPROX_SIMPLE的结果,只有4个点。

1
2
3
4
5
6
7
8
# 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()用于绘制轮廓。

1
2
3
4
5
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、轮廓面积

1
area = cv.contourArea(cnt)

轮廓特征计算的结果并不等同于像素点的个数,而是根据几何方法算出来的,所以有小数。

2、轮廓周长

1
perimeter = cv.arcLength(cnt, True)

参数2表示轮廓是否封闭

3、外接矩形

形状的外接矩形有两种,如下图,绿色的叫外接矩形,表示不考虑旋转并且能包含整个轮廓的矩形。蓝色的叫最小外接矩,考虑了旋转。

1
2
x, y, w, h = cv.boundingRect(cnt)  # 外接矩形
cv.rectangle(img2, (x, y), (x + w, y + h), (0, 255, 0), 2)
1
2
3
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、最小外接圆

外接圆跟外接矩形一样,找到一个能包围物体的最小圆:

1
2
3
(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、外接椭圆

我们可以用得到的轮廓拟合出一个椭圆:

1
2
ellipse = cv.fitEllipse(cnt)
cv.ellipse(img2, ellipse, (255, 255, 0), 2)

6、判断点和轮廓关系

1
result = cv.pointPolygonTest(biggest, (w,h), False)

第一个参数是某一轮廓。第二个参数是像素点坐标。第三个参数如果为True则输出该像素点到轮廓最近距离;如果为False,则输出为正表示在轮廓内,0为轮廓上,负为轮廓外。

0%