引言
这一篇文章我们来讨论图像轮廓相关的知识点,什么叫做轮廓,从定义上来说它是指图像中连续的曲线或边界,表示了图像中目标的形状和外观特征。我们之前已经介绍了一些图像边缘检测相关的内容,我们有一些零零散散的一些线段也可以当做是一个边缘,但是这些不能看做是图像轮廓,因为轮廓首先得是一个整体,是连在一块的,这就是轮廓和边缘之间的区别。我们只要记住:
边缘: 零零散散的
轮廓: 它是一个整体
1.轮廓检测
接下来我们首先要看的的就是轮廓检测,我们调用OpenCV的 cv2.findContours()
函数来查找图像中的轮廓。这个函数需要输入一个二值化的图像(通常是经过边缘检测后的结果),并返回一组轮廓。可以选择不同的检索模式和轮廓逼近方法来控制轮廓的提取方式。语法如下:
cv2.findContours(image,mode,method)
-
image
:输入图像,为了提高准确率,使用二值图像。 -
mode
:定义轮廓检索模式,有四种可选模式:cv2.RETR_EXTERNAL
:只检索最外面的轮廓;cv2.RETR_LIST
:检索所有轮廓,并将其保存在列表中。cv2.RETR_CCOMP
:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;cv2.RETR_TREE
:检索所有的轮廓,并重构嵌套轮廓的整个层次,我们常用这个
-
method
:定义轮廓逼近方法,我们这里主要使用下列两种方法cv2.CHAIN_APPROX_NONE
:存储所有的边界点。cv2.CHAIN_APPROX_SIMPLE
:仅存储水平、垂直和对角线方向的端点。
下面我们来看一下具体的代码部分:
将原始图片转换为二值图像
这里为了方便演示,使用了一个非常规则的图片,包含几种基础的集合图形
import cv2
def cv_show(img,name):
cv2.imshow(name,img)
# 第一步:转换为二值图
img = cv2.imread('my_contour.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv_show(thresh,'contours')
轮廓检测
调用cv2.findContours()
函数
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
- binary:返回的就是二值图像
- contours:返回的是我们的轮廓信息
- hierarchy:返回的是层级结构
绘制轮廓
使用 cv2.drawContours()
函数可以将提取到的轮廓绘制在图像上,这样就可以可视化检测到的目标的形状和位置。主要包含下列几个参数
draw_img
:要在其上绘制轮廓的图像。contours
:要绘制的轮廓列表。contourIdx
:指定要绘制的轮廓的索引。使用-1
表示绘制所有的轮廓。color
:绘制轮廓的颜色,格式为(B, G, R)
,其中 B、G 和 R 分别表示蓝色、绿色和红色的强度。例如(255, 0, 0)
表示纯蓝色。thickness
:轮廓线的粗细,默认值为1
。
# 传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
draw_img = img.copy() #创建一个图像副本保存
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) #绘制所有轮廓
cv_show(res,'res')
上述我们绘制了所有的轮廓,有的时候我们只想得到其中的某一个轮廓,我们可以指定相应的轮廓索引,例如
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, 0, (255, 0, 0), 2)#绘制索引为0的轮廓
cv_show(res,'res')
2.轮廓特征
在我们得到轮廓之后,如何利用这些信息呢,像其他图像特征一样,图像轮廓提供了许多与目标形状相关的特征信息,可以用于形状分析、目标检测和图像识别等任务。以下是一些常见的轮廓特征以及计算:
-
面积(Area):轮廓所包围的区域的面积。
- 可以使用
cv2.contourArea(contour)
函数计算轮廓的面积。
- 可以使用
-
周长(Perimeter):轮廓的周长,即轮廓的闭合曲线的长度。
- 可以使用
cv2.arcLength(contour, closed)
函数计算轮廓的周长,其中closed
参数指定轮廓是否为闭合曲线。
- 可以使用
-
边界框(Bounding Box):将轮廓包围在一个矩形框中。
- 可以使用
cv2.boundingRect(contour)
函数获取包围轮廓的最小矩形框的位置和大小。
- 可以使用
-
最小外接圆(Minimum Enclosing Circle):包围轮廓的最小半径的圆。
- 可以使用
cv2.minEnclosingCircle(contour)
函数获取包围轮廓的最小外接圆的圆心和半径。
- 可以使用
-
最小外接矩形(Minimum Enclosing Rectangle):包围轮廓的最小面积的矩形。
- 可以使用
cv2.minAreaRect(contour)
函数获取包围轮廓的最小外接矩形的位置、大小和旋转角度。
- 可以使用
-
凸包(Convex Hull):能够完全包围轮廓的凸多边形。
- 可以使用
cv2.convexHull(points)
函数获取轮廓的凸包。
- 可以使用
-
近似多边形(Approximation Polygon):用直线段逼近轮廓的多边形。
- 可以使用
cv2.approxPolyDP(curve, epsilon, closed)
函数对轮廓进行近似,其中epsilon
是逼近精度参数。
- 可以使用
-
形心(Centroid):轮廓所包围区域的重心或平均位置。
- 可以使用
cv2.moments(contour)
函数计算轮廓的矩,然后通过计算质心来获取重心。
- 可以使用
这里我们以前两个为例演示如何使用,其他的特征计算都是直接调用相应的函数即可。 在上面我们已经得到了轮廓的信息contours
,但是我们不能直接将其进行特征计算,因为它包含了所有的轮廓信息,因此我们要指定相应的索引再进行特征计算
cnt = contours[0] #指定索引为0的轮廓
#面积
print(cv2.contourArea(cnt))
#周长,True表示闭合的
print(cv2.arcLength(cnt,True))
24578.0
586.4995617866516
3.轮廓近似
轮廓近似是指用直线段或曲线段逼近实际轮廓的过程,从而减少轮廓中的点数并简化轮廓的表示。这可以帮助我们更有效地处理和分析轮廓。
在 OpenCV 中,可以使用 cv2.approxPolyDP(curve, epsilon, closed)
函数来进行轮廓的近似。参数说明如下:
curve
:输入的轮廓。epsilon
:表示近似精度的参数。较小的值会产生更准确的逼近,但会保留更多的细节和点。较大的值会导致更粗糙的逼近,但会减少点的数量。closed
:一个布尔值,指示是否闭合轮廓。
下面我们来看一个具体例子
img = cv2.imread('contours2.png')
# 获取轮廓特征
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
# 原始轮廓
draw_img = img.copy()
original = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
# 近似轮廓
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
approx = cv2.drawContours(draw_img, [approx], -1, (0, 255, 0), 2)
res = np.hstack((original,approx))
cv_show(res,'res')
左边是原始轮廓,右边是近似轮廓,可以看出对于轮廓变化较大的区域,近似效果有一定偏差,如果我们将epsilon设置的更小一点,如下所示
此时近似效果非常接近。
4.外接矩形
很多时候,我们不是直接利用图像的轮廓进行操作,因为这些图像往往不规则,我们需要对其外接矩形或外接圆来进行分析,在opencv中,我们可以利用cv2.boundingRect()
函数得到轮廓的外接矩形信息。
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')
总结
我们介绍了如何进行图形轮廓的检测、特征计算、轮廓近似处理。总的来说,图像轮廓具有以下特点:
- 轮廓是由连续的曲线或边界组成的,能够准确地描述目标的形状。
- 轮廓可以用于目标的识别、分类和形状分析。
- 轮廓可以通过计算形状的特征(如面积、周长、重心等)来获取更多的信息。
- 可以使用图像轮廓进行图像分割、边缘检测和图像增强等任务。