OpenCv(六)——轮廓检测、轮廓近似、绘制外接圆外接矩形

目录

一、轮廓检测

1、原理

参数解释

2、代码实现

(1)读取并显示原始图像和二值化处理的结果

(2)查找轮廓

(3)轮廓的绘制并显示

(4)根据面积筛选特定轮廓

二、轮廓特征

1、轮廓的面积

2、轮廓的周长

三、轮廓的近似

1、原理

2、参数

3、代码实现

四、外接圆、外接矩形的绘制


一、轮廓检测

1、原理

边缘检测:主要是通过一些手段检测数字图像中明暗变化剧烈(即梯度变化比较大)像素点,偏向于图像中像素点的变化。 轮廓检测:指检测图像中的对象边界。

使用轮廓检测可以获得物体的边界,方便在图像中对他们进行定位。通常在一些有趣的应用中轮廓检测是第一处理环节。比如图像前景提取,简单的图像分割,检测以及识别等。

查找轮廓的函数:cv2.findContours( )

轮廓的绘制:cv2.drawContours( )

参数解释

        查找轮廓的API:image, contours, hierarchy = cv2.findContours(img, mode, method)
参数:img:需要实现轮廓检测的原图
        mode: 轮廓的检索模式,主要有四种方式:
                   cv2.RETR_EXTERNAL:只检测外轮廓,所有子轮廓被忽略
                   cv2.RETR_LIST:检测的轮廓不建立等级关系,所有轮廓属于同一等级
                   cv2.RETR_CCOMP:返回所有的轮廓,只建立两个等级的轮廓。一个对象的外轮廓为第1级组织结构。而对象内部中空洞的轮廓为第2级组织结构,空洞中的任何对象的轮廓又是第 1 级组织结构。
                    cv2.RETR_TREE:返回所有的轮廓,建立一个完整的组织结构的轮廓。

       method:轮廓的近似方法,主要有以下两种:
                    cv2.CHAIN_APPROX_NONE:存储所有的轮廓点。
                   cv2.CHAIN_APPROX_SIMPLE:压缩模式,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。

返回值:

        image:返回处理的原图
        contours:包含图像中所有轮廓的list对象。其中每一个独立的轮廓信息以边界点坐标(x,y)的形式储存在numpy数组中。

        hierarchy:轮廓的层次结构。一个包含4个值的数组:[Next, Previous, First Child, Parent]
                  Next:与当前轮廓处于同一层级的下一条轮廓
                  Previous:与当前轮廓处于同一层级的上一条轮廓
                  First Child:当前轮廓的第一条子轮廓
                  Parent:当前轮廓的父轮廓
注意:做轮廓检测前需要将图片读取为二值数据,即像素值只为0和255(非黑即白)。

2、代码实现

(1)读取并显示原始图像和二值化处理的结果

import cv2
phone = cv2.imread('../data/ja.jpg')#读取原图

#两种灰度处理办法既可以使用imread还可以使用cvtcolor进行颜色转换
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)#灰度图的处理
# phone_gray=cv2.imread('phone.png',0)  #读取灰度图

#图像的二值化处理
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)#阈值处理为二值

#显示二值化处理的结果
cv2.imshow('phone_binary',phone_binary)
cv2.waitKey(0)

效果(左侧为原图,右侧为二值化处理后的图)

 

(2)查找轮廓

_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
print(hierarchy)
print(len(contours))

总共找到了9个轮廓,将轮廓绘制出的点存储在contours这个变量中,hierarchy存储了各个轮廓之间的层级结构。

(3)轮廓的绘制并显示

        cv2.drawContours(image, contours, contourIdx, color, thickness=None,
                  lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数含义如下:
image:要在其上绘制轮廓的输入图像。
contours:轮廓列表,通常由cv2.findContours()函数返回。
contourIdx:要绘制的轮廓的索引。如果为负数,则绘制所有轮廓。 -1
color:轮廓的颜色,以BGR格式表示。例如,(0, 255, 0)表示绿色。
thickness:轮廓线的粗细。默认值为1。如果值为-1,则是填充绘制的轮廓。
lineType:轮廓线的类型。默认值为cv2.LINE_8。
hierarchy:轮廓层次结构。通常由cv2.findContours()函数返回。
maxLevel:绘制的最大轮廓层级。默认值为None,表示绘制所有层级。
offset:轮廓点的偏移量。默认值为None。

image_copy = phone.copy()    #复制原图,在复制的图像上绘制轮廓,防止原图像被破坏
image_copy = cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('Contours_show', image_copy)
cv2.waitKey(0)

轮廓检测绘制出所有的轮廓

通过调整contourIdx的值可以取出图像中任意一个轮廓

 

(4)根据面积筛选特定轮廓

a_list=[]

for i in contours:
    if cv2.contourArea(i)>10000:
        a_list.append(i)
image_copy = phone.copy()
image_copy = cv2.drawContours(image=image_copy, contours=a_list, contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('Contours_show_10000', image_copy)
cv2.waitKey(0)

筛选出面积大于10000的轮廓

二、轮廓特征

获取轮廓后,通常基于轮廓的特征进行筛选、识别和处理。例如,基于轮廓的周长和面积对轮廓进行筛选,然后绘制筛选的目标轮廓或其最小外接矩形。

1、轮廓的面积

cv2.contourArea(contour[第几个轮廓, oriented])       面积
contour:顶点构成的二维向量组(如轮廓列表contours中的一个轮廓)
oriented:定向区域标志,默认值为 False,返回面积的绝对值,Ture 时则根据轮廓方向返回带符号的数值。

area_0 = cv2.contourArea(contours[0])
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)

2、轮廓的周长

arcLength(curve,closed)

周长 curve,输入的二维点集(轮廓顶点),可以是 vector 或 Mat 类型。

closed,用于指示曲线是否封闭。布尔类型,True和False。

length = cv2.arcLength(contours[0],closed=True)
print(length)

三、轮廓的近似

1、原理

轮廓近似:指对轮廓进行逼近或拟合,得到近似的轮廓。在图像处理中,轮廓表示了图像中物体的边界,因此轮廓近似可以用来描述和识别物体的形状。

2、参数

approx = cv2.approxPolyDP(curve, epsilon, closed)

参数说明:

curve:输入轮廓。

epsilon:近似精度,即两个轮廓之间最大的欧式距离。该参数越小,得到的近似结果越接近实际轮廓;反之,得到的近似结果会更加粗略。

closed:布尔类型的参数,表示是否封闭轮廓。如果是 True,表示输入轮廓是封闭的,近似结果也会是封闭的;否则表示输入轮廓不是封闭的,近似结果也不会是封闭的。

返回值:approx:近似结果,是一个ndarray数组,为1个近似后的轮廓,包含了被近似出来的轮廓上的点的坐标

3、代码实现

(1)原图的处理及查找轮廓

phone = cv2.imread('phone.png')
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY) #转换为灰度图
ret,phone_thresh = cv2.threshold(phone_gray,120,255,cv2.THRESH_BINARY)  #二值化

image, contours, hierarchy = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)#获取轮廓

(2)按照面积降序排序,查找最大的面积

contours_with_area=[(cnt,cv2.contourArea(cnt)) for cnt in contours]
#对轮廓进行遍历,将轮廓的序号和面积存储在一个元组中,每一个轮廓是列表中的一个元素

sorted_contours=sorted(contours_with_area,key=lambda x:x[1],reverse=True)
#按照面积对轮廓进行降序排序

aa=sorted_contours[0][0]
#取出列表第一个元素的第二个值,最大面积

(3)轮廓的近似

epsilon = 0.01 * cv2.arcLength(contours[0],True)        #设置近似精度
approx = cv2.approxPolyDP(contours[0], epsilon, True)   #对轮廓进行近似,返回轮廓
print(approx.shape)
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new,[approx],contourIdx=-1,color=(0,255,0),thickness=3)#绘制轮廓
cv2.imshow('phone',phone)
cv2.waitKey(0)
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)

通过approxPolyDp函数中的epsilon这个参数控制绘制的精度,类似于数学中的微元法,epsilon中的0.01这个值来控制微元的精度,这个值越小绘制轮廓的点越多越精细,值越大点越少,绘制的会更粗糙。

epsilon参数的精度为0.05,可以看出图中的轮廓只找到了4个点

epsilon参数的精度为0.01,减小了精度,轮廓的点数增加到了7个

四、外接圆、外接矩形的绘制

1、绘制外接圆

绘制外接圆传入参数时要注意,圆心(x,y)和半径r都必须时整数类型。

cnt = contours[6]    #这里选择了第7个轮廓

(x,y),r = cv2.minEnclosingCircle(cnt)#计算轮廓的外接圆
phone_circle = cv2.circle(phone,(int(x),int(y)),int(r),(0,255,0),2)#绘制外接圆的方法
cv2.imshow('phone_circle',phone_circle)
cv2.waitKey(0)

2、绘制外接矩形

x,y,w,h = cv2.boundingRect(cnt)#计算轮廓的最小外接矩形
phone_rectangle = cv2.rectangle(phone,(x,y),(x+w,y+h),(0,255,0),2)  #在图像上绘制矩形
cv2.imshow('phone_rectangle',phone_rectangle)
cv2.waitKey(0)