轮廓是什么?
轮廓可以简单地解释为一条连接所有连续点(沿边界)的曲线,具有相同的颜色或强度。轮廓是一个有用的工具,它可以用于形状分析和目标检测和识别。
让我们看看如何找到二值图像的轮廓(必须是二值图像作为输入图像)
image, contours, hierarchy = cv2.findContours ( InputOutputArray image,
int mode,
int method,
Point offset = Point()
)
参数 | 说明 |
---|---|
image | 源,一个8位的单通道图像。非零像素被视为1。零像素仍然是0,所以图像被视为二进制。您可以使用compare 、inRange 、threshold 、adaptiveThreshold 、Canny 和其他方法创建基于灰度或彩色的二值图像。如果mode等于RETR_CCOMP 或RETR_FLOODFILL ,则输入也可以是32位整数的标签图像(CV_32SC1)。 |
contours | 检测到的轮廓。每个轮廓被存储为点的向量 |
hierarchy | 可选输出向量,包含关于图像拓扑的信息。它的元素和轮廓的数量一样多。对于每一个第i条轮廓线[i],将元素hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]、hierarchy[i][3]分别设置为下一个轮廓线和上一个轮廓线(即第一个子轮廓线和父轮廓线)的基于0的轮廓线索引。如果轮廓i没有下一个、上一个、父轮廓或嵌套轮廓,层次结构[i]中的相应元素将为负。 |
mode | 轮廓检索模式,参见RetrievalModes |
method | 轮廓近似方法,参见ContourApproximationModes |
offset | 每个轮廓点被移动的可选偏移量。如果从图像感兴趣区提取轮廓,然后在整个图像环境中分析轮廓,这是非常有用的。 |
在二值图像中寻找轮廓。
该函数使用算法[169]从二值图像中检索轮廓。轮廓是一个有用的工具,用于形状分析和目标检测和识别。
注意:我使用的是3.4.1.15版本Opencv,在最新版本的Opencv中(我没有具体的调查从哪一版本开始的)返回参数只有两个(contours, hierarchy)
RetrievalModes
(轮廓检索算法的模式)
RetrievalModes 参数 |
说明 |
---|---|
cv2.RETR_EXTERNAL | 只获取外部轮廓。它设置所有轮廓的hierarchy[i][2]=hierarchy[i][3]=-1。 |
cv2.RETR_LIST | 检索所有轮廓而不建立任何层次关系,将其保存到一条链表当中。 |
cv2.RETR_CCOMP | 检索所有轮廓并将它们组织到一个两级层次结构中。顶层是各部分的外部边界。第二层是空洞的边界。如果有另一个轮廓在一个连接组件的孔内,它仍然放在顶层。 |
cv2.RETR_TREE | 检索所有轮廓并重建嵌套轮廓的完整层次结构。 |
cv2.RETR_FLOODFILL |
ContourApproximationModes
(轮廓逼近算法)
ContourApproximationModes 参数 |
说明 |
---|---|
cv2.CHAIN_APPROX_NONE | 绝对存储所有的轮廓点。即轮廓的任意两个后续点(x1,y1)和(x2,y2)均为水平、垂直或对角线邻居,即max(abs(x1-x2),abs(y2-y1))==1。 |
cv2.CHAIN_APPROX_SIMPLE | 压缩水平、垂直和对角线的线段,只留下它们的端点。例如,一个向上的矩形轮廓用4个点进行编码。 |
cv2.CHAIN_APPROX_TC89_L1 | 应用Teh-Chin链近似算法之一[177] |
cv2.CHAIN_APPROX_TC89_KCOS | 应用Teh-Chin链近似算法之一[177] |
上面,我们说轮廓是一个具有相同强度的形状的边界。它存储形状边界的(x,y)坐标。但是它是否存储了所有的坐标呢?这是由轮廓近似法规定的。
如果你通过cv2.CHAIN_APPROX_NONE
,所有边界点都被存储。但实际上我们需要所有的点吗?例如,你找到了一条直线的轮廓。需要用直线上的所有点来表示这条直线吗?不,我们只需要这条直线的两个端点。这就是cv2.CHAIN_APPROX_SIMPLE
。它去除所有冗余点并压缩轮廓,从而节省内存。
下面的矩形图像演示了这种技术。只需在轮廓数组的所有坐标上画一个圆(用蓝色绘制)。第一张图片显示了我在cv2.CHAIN_APPROX_NONE
(734点)和第二张图片显示了一个cv2.CHAIN_APPROX_SIMPLE
(只有4点)。看它节省了多少内存!!
所以,一般情况下更建议使用cv2.CHAIN_APPROX_SIMPLE
方法!!
#图像轮廓
import numpy as np
import cv2
im = cv2.imread('contours.png')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) #转化为灰度图
ret, thresh = cv2.threshold(imgray, 127, 255, 0) #图像二值化,127为界,大于127置为255,小于127置为0
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv.findContours()函数有三个参数,第一个是源图像,第二个是轮廓检索模式,第三个是轮廓近似方法。然后输出修改后的图像,轮廓和层次结构。contours是图像中所有轮廓的Python列表。每个单独的轮廓都是对象边界点的(x,y)坐标的Numpy数组。
注:我们将在后面详细讨论第二个和第三个参数以及层次结构。在此之前,代码示例中给它们的值将适用于所有图像。
注意:在drawContours之前,输入图像一定要.copy(),不然原图像就变了!!!
image = cv2.drawContours ( InputOutputArray image,
InputArrayOfArrays contours,
int contourIdx,
const Scalar & color,
int thickness = 1,
int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX,
Point offset = Point()
)
参数 | 说明 |
---|---|
image | 目标图像 |
contours | 所有的输入轮廓。每个轮廓都以点向量的形式存储。 |
contourIdx | 指示要绘制轮廓的参数。如果为负,则绘制所有的轮廓线。 |
color | 轮廓的颜色。 |
thickness | 绘制轮廓线所用线的粗细。如果是负的(例如,厚度=填充),则绘制内部轮廓。 |
lineType | 连接线. 见 LineTypes |
hierarchy | 关于层次结构的可选信息。只有当你只想绘制部分轮廓时才需要它(参见maxLevel)。 |
maxLevel | 绘制轮廓线的最大级别。如果为0,则只绘制指定的轮廓。如果值为1,函数将绘制等值线和所有嵌套等值线。如果值为2,则该函数绘制轮廓线、所有嵌套轮廓线、所有嵌套到嵌套的轮廓线,以此类推。仅当存在可用的层次结构时才考虑此参数。 |
offset | 可选轮廓位移参数。将所有绘制的轮廓线移动到指定的偏移量=(dx,dy)。 |
LineTypes
线类型
LineTypes 参数 |
说明 |
---|---|
cv2.FILLED | |
cv2.LINE_4 | 4-connected line |
cv2.LINE_8 | 8-connected line |
cv2.LINE_AA | 锯齿线 |
绘制轮廓线或填充轮廓线。
如果厚度≥0,则在图像中绘制轮廓线轮廓线;如果厚度<0,则填充轮廓线所包围的区域。
当 thickness=FILLED
时,该函数被设计来正确处理带有孔的连接组件,即使没有提供层次数据。这是通过使用偶奇规则分析所有的轮廓来完成的。如果你有一个单独检索的轮廓的联合集合,这可能会给出不正确的结果。为了解决这个问题,您需要为每个轮廓子组分别调用drawContours
,或者使用contourIdx参数遍历集合。
为了画出轮廓线,cv2.drawContours
函数。它也可以用于绘制任何形状,只要你有它的边界点。它的第一个参数是source image,第二个参数是应该作为Python列表传递的轮廓,第三个参数是轮廓索引(在绘制单个轮廓时很有用)。要绘制所有轮廓线,输入-1),剩下的参数是颜色、厚度等。
cv.drawContours(img, contours, -1, (0,255,0), 3)
cv.drawContours(img, contours, 3, (0,255,0), 3)
cnt = contours[4]
cv.drawContours(img, [cnt], 0, (0,255,0), 3)
注:要画一个单独的轮廓线,最后两个方法是相同的,但是你会发现最后一个方法更常用。
#图像轮廓
#Opencv版本3.4.1.15
import numpy as np
import cv2
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
im = cv2.imread('contours.png')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) #转化为灰度图
ret, thresh = cv2.threshold(imgray, 127, 255, 0) #图像二值化,127为界,大于127置为255,小于127置为0
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #新版本的Opencv返回参数只有contours, hierarchy
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# 注意需要copy,要不原图会变。。。
img = im.copy()
res = cv2.drawContours(img, contours,-1, (0, 0, 255), 2) #红色轮廓
#要画一个单独的轮廓线,比如第四个轮廓线:
img = cv2.drawContours(img, contours, 3, (0,255,0), 2) #绿色轮廓
#常用这种方法画一个单独的轮廓
cnt = contours[0]
cv2.drawContours(img, [cnt], 0, (255,0,0), 2) #蓝色轮廓
cv_show(res,'res')
运行结果:
主要参考于OpenCV官方网站:http://www.opencv.org.cn/
目前博主已更新OpenCV平滑处理函数、形态学操作函数的详细介绍,链接如下:
【OpenCV-图像处理】图像平滑处理函数
【OpenCV-图像处理】形态学变换函数
【OpenCV-图像处理】图像阈值处理
【OpenCV-图像处理】如何计算图像梯度,以及如何使用梯度来检测边缘
【OpenCV-图像处理】Canny 边缘检测
【OpenCV-图像处理】图像金字塔
<后续还会继续翻译和整理【OpenCV-图像处理】相关内容,如果需要,可持续关注我哦~> |
<翻译和整理不易,留个赞或评论支持一下我吧^^>
如有疑问,欢迎批评指正^^