原文链接:http://www.juzicode.com/opencv-python-findcontours-drawcontours
图像轮廓用来描述图像中连续的点,它们有同样的颜色和灰度级。为了更精确地进行检测,在查找轮廓前需要先将图像做二值化处理或者使用canny边沿检测。在OpenCV中轮廓检测只查找白色目标,黑色背景会被忽略。
1、查找轮廓 findContours()
contours,hierarchy=cv2.findContours(image,mode,method[,contours[,hierarchy[,offset]]])
- 参数含义:
- image:输入图像,8bit单通道;图像元素的值为0表示背景,非0值表示前景,为了精确查找轮廓,最好对原始图像做二值化处理。如果mode参数为RETR_CCOMP或RETR_FLOODFILL输入图像也可以是32bit单通道图像(CV_32SC1)。但是floodfill不支持cv-8u类型
- mode:轮廓提取模式。
- method:轮廓近似方法。
- contours:找到的轮廓。
- hierarchy:找到轮廓层次结构。
- offset:偏移值,使用ROI时用来计算原始位置。
mode的名称和含义:
mode | 含义 |
cv2.RETR_EXTERNAL | 只提取外部轮廓,设置hierarchy[i][2]=hierarchy[i][3]=-1 |
cv2.RETR_LIST | 提取所有轮廓,不包含轮廓间的层次关系 |
cv2.RETR_CCOMP | 提取所有轮廓,包含2层层次关系,顶层是外围边界,底层是内部hole。如果有多层嵌套,仍然按照2层组织。 |
cv2.RETR_TREE | 按照完整的层次关系组织 |
cv2.RETR_FLOODFILL |
method 的名称和含义:
method | 含义 |
cv2.CHAIN_APPROX_NONE | 存储所有的边间点,不管是垂直方向、水平方向或者对角线方向 |
cv2.CHAIN_APPROX_SIMPLE | 垂直方向、水平方向或对角线方向只保留终点,比如一个长方形就只包含4个顶点 |
cv2.CHAIN_APPROX_TC89_L1 | 使用teh-Chini近似算法 |
cv2.CHAIN_APPROX_TC89_KCOS | 使用teh-Chini近似算法 |
下面是一个查找轮廓的例子,读入图像,灰度化后再进行二值化,然后调用findContours()查找轮廓:
import numpy as np
import cv2
print('VX公众号: 桔子code / juzicode.com')
#读入图像、灰度、二值化
img_src = cv2.imread('..\\samples\\picture\\contours1.bmp')
img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
val, img_bin = cv2.threshold(img_gray,127,255, cv2.THRESH_BINARY)
#cv2.imwrite('contours-bin.bmp',img_bin)
#查找轮廓
contours, hierarchy=cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE )
print('contours:',contours)
print('type(contours):',type(contours))
print('len(contours):',len(contours))
print('type(contours[0]):',type(contours[0]))
print('contours[0].shape:',contours[0].shape)
print('hierarchy:',hierarchy)
print('type(hierarchy):',type(hierarchy))
print('len(hierarchy):',len(hierarchy))
print('hierarchy.shape:',hierarchy.shape)
运行结果:
VX公众号: 桔子code / juzicode.com
contours: [array([[[159, 150]],
[[159, 241]],
[[307, 241]],
[[307, 150]]], dtype=int32), array([[[330, 28]],
[[330, 100]],
[[434, 100]],
[[434, 28]]], dtype=int32), array([[[ 23, 20]],
[[ 23, 114]],
[[172, 114]],
[[172, 20]]], dtype=int32)]
type(contours): <class 'list'>
len(contours): 3
type(contours[0]): <class 'numpy.ndarray'>
contours[0].shape: (4, 1, 2)
hierarchy: [[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[-1 1 -1 -1]]]
type(hierarchy): <class 'numpy.ndarray'>
len(hierarchy): 1
hierarchy.shape: (1, 3, 4)
contours是找到的轮廓列表,是由n个轮廓组成的list,每个轮廓是一个numpy数组,它的shape为(m,1,2),m为轮廓构成的像素的个数,每个点有2个数值,分别为x和y坐标。
hierarchy的shape为(1,n,4),n对应contours的个数n,所以要访问第i个轮廓的hierarchy时用hierarchy[0][i]表示。每个hierarchy的元素有4个值组成,依次为next,previous,first child和parent,表示下一个节点,上一个节点,第一个子节点,父节点。
2、绘制轮廓 drawContours
drawContours(image,contours,contourIdx,color[,thickness[,lineType[,hierarchy[,maxLevel[,offset]]]]])->image
- 参数含义:
- image:要绘制边框的图像,调用后会修改该图像。
- contours:轮廓列表,一般传入findContours()找到的边框。
- contourIdx:contours的索引,也就是要表示的第几个边框,如果要绘制所有的边框,设置为负数即可。
- color:颜色,bgr三个参数构成的tuple。
- thickness:边框的宽度。
- lineType:边界连线类型。
- hierarchy:依赖maxLevel使用,如果maxLevel设置为1和2时用到该参数,
- maxLevel:
- offset:边框偏移位置
下面这个例子查找轮廓后依次绘制轮廓:
import numpy as np
import cv2
print('VX公众号: 桔子code / juzicode.com')
#读入图像、灰度、二值化
img_src = cv2.imread('..\\samples\\picture\\contours1.bmp')
cv2.imshow('img_src',img_src)
img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
val, img_bin = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY)
#查找轮廓
contours, hierarchy=cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE )
print('len(contours):',len(contours))
#绘制轮廓
for ind in range(len(contours)):
cv2.drawContours(img_src, contours, ind, (0,0,255), 3)
cv2.imshow('con',img_src)
cv2.waitKey()
运行结果:
还可以将contourIdx设置为-1,一次性绘制所有轮廓:
#设置为-1,绘制所有轮廓
cv2.drawContours(img_src, contours, -1, (0,0,255), 3)
cv2.imshow('con',img_src)
cv2.waitKey()
设置offset参数:
cv2.drawContours(img_src, contours, -1, (0,0,255), 3, offset=(10,10))
绘制的边框整体偏移了offset个像素: