目录
一、轮廓检测
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)