十八 轮廓发现
一、轮廓发现
是基于图像边缘提取的基础,寻找对象轮廓的方法,所以边缘提取的阈值选定会影响最终轮廓的发现。
操作步骤
- 1.转换图像为二值化图像:threshold方法或者canny边缘提取获取的都是二值化图像
- 2.通过二值化图像寻找轮廓:findContours
- 3.描绘轮廓:drawContours
二、相关函数
1、findContours寻找轮廓
直接使用二值化图像 cv.findContours(binary,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)
使用边缘检测后的图像 def findContours(image, mode, method, contours=None, hierarchy=None, offset=None):
image:输入图像,图像必须为8-bit单通道图像,图像中的非零像素将被视为1,0像素保留其像素值,故加载图像后会自动转换为二值图像。可以通过threshold和canny获取
mode:轮廓检索模式
- RETR_EXTERNAL:表示只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
- RETR_LIST:提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
- RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
- RETR_TREE:提取所有轮廓并重新建立网状轮廓结构
- RETR_FLOODFILL:官网没有介绍,应该是洪水填充法
method:轮廓近似方法
- CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
- CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
- CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS使用Teh-Chinl链逼近算法中的一种
返回值
ret = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cloneImage,contours,heriachy = cv.findContours(binary,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE) #RETR_TREE包含检测内部
- 返回一个元组,内部有三个元素
- <class 'numpy.ndarray'>
- <class 'list'>
- <class 'numpy.ndarray'>
第一个返回值:cloneImage是我们传入的二值化图像
第二个返回值:contours是一个列表,是轮廓本身,含有轮廓上面的各个点的位置信息
第三个返回值:heriachy是每条轮廓对应的属性
2、drawContours绘制轮廓
def drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None):
1.image:输入输出图像,Mat类型即可
2.contours:使用findContours检测到的轮廓数据,每个轮廓以点向量的形式存储
3.contourIdx:绘制轮廓的只是变量,如果为负值则绘制所有输入轮廓(就是i,仅仅表示一个序号)
4.color:轮廓颜色
5.thickness:绘制轮廓所用线条粗细度,如果值为负值,则在轮廓内部绘制
cv.drawContours(image,contours,i,(0,0,255),2) #绘制轮廓
cv.drawContours(image,contours,i,(0,0,255),-1) #填充轮廓
三、代码实现
1、使用直接使用阈值方法threshold方法获取二值化图像来选择轮廓
import cv2 as cv import numpy as np def contours_demo(image): # 高斯模糊,消除噪声 dst = cv.GaussianBlur(image,(9,9),15) # 先变灰度图像 gray = cv.cvtColor(dst,cv.COLOR_BGR2GRAY) #OTSU大律法获取二值图像 #args # 输入图像 # 阈值(为0是全局自适应阈值, 参数0可改为任意数字但不起作用) # 与THRESH_BINARY和THRESH_BINARY_INV阈值类型一起使用设置的最大值。 # 阈值类型 ret,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) cv.imshow('binary image',binary) #寻找轮廓(直接输入二值图像) #args: # 输入的二值图像 # 轮廓检索模式 RETR_TREE:提取所有轮廓并重新建立网状轮廓结构 # 轮廓近似方法 CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息 #return: # 传入的二值图像(ndarray) # list:轮廓本身,含有轮廓上面各个点的位置信息 # 每条轮廓对应的属性(ndarray) #cloneimage,contours,heriachy = cv.findContours(binary,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE) #RETR_TREE包含检测内部 cloneImage, contours, heriachy = cv.findContours(binary, cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) # RETR_EXTERNAL检测外部轮廓 for i ,contour in enumerate(contours): #cv.drawContours(image,contours,i,(0,0,255),2) #绘制轮廓 cv.drawContours(image,contours,i,(0,0,255),-1) #填充轮廓 print(i) cv.imshow('detect image',image) src = cv.imread('circle.png') cv.imshow('input image',src) contours_demo(src) cv.waitKey(0) cv.destroyAllWindows()
如果使用绘制轮廓的话:
2、使用canny边缘检测获取二值化图像
1 import cv2 as cv 2 import numpy as np 3 4 #提取边缘信息 5 def edge_demo(image): 6 #1、高斯模糊 7 dst = cv.GaussianBlur(image,(3,3),0) 8 #2、灰度化 9 gray = cv.cvtColor(dst,cv.COLOR_BGR2GRAY) 10 #3、canny边缘提取出来的是二值图像 11 edge_output = cv.Canny(gray,50,108) 12 13 cv.imshow('edge_info',edge_output) 14 return edge_output 15 16 #轮廓发现绘制 17 def contours_demo(image): 18 binary = edge_demo(image) 19 # 检测轮廓 20 # 寻找轮廓(输入canny边缘提取之后的二值图像) 21 # args: 22 # 输入的二值图像 23 # 轮廓检索模式 RETR_TREE:提取所有轮廓并重新建立网状轮廓结构 24 # 轮廓近似方法 CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息 25 # return: 26 # 传入的二值图像(ndarray) 27 # list:轮廓本身,含有轮廓上面各个点的位置信息 28 # 每条轮廓对应的属性(ndarray) 29 cloneimage,contours,heriachy = cv.findContours(binary,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE) #RETR_TREE包含检测内部 30 for i,contour in enumerate(contours): 31 #cv.drawContours(image,contours,i,(0,0,255),2) #绘制轮廓 32 cv.drawContours(image,contours,i,(0,0,255),-1) #填充轮廓 33 print(i) 34 cv.imshow('detect contours',image) 35 36 img = cv.imread('contours.png') 37 cv.imshow('input image',img) 38 contours_demo(img) 39 cv.waitKey(0) 40 cv.destroyAllWindows()