查找图像的轮廓在图像处理及应用中扮演着重要的角色。那么什么是轮廓(contour)?《Learning OpenCV 3》中给出的定义是这样的——轮廓即是以某种方式表示图像中的曲线的点的列表。这种表示可以根据实际的情形不同而不同。表示一条曲线的方式有很多种。OpenCV中,轮廓是由STL风格的vector<>模板对象表示的,其中vector中的每个元素都编码了曲线上,下一点的位置信息。查找图像轮廓的函数是cv::findContours(),并通过cv::drawContours()将查找到的轮廓绘制到图像上
1985年,有个叫satoshi suzuki的人发表了一篇论文,Topological structural analysis of digitized binary images by border following,他介绍了两种算法来实现轮廓的提取,当然输入的图像是二值图像。findcontour就是基于这篇论文的思路来实现。
本博文分为三部分来写,第一部分是介绍satoshi suzuki论文的大致思想;第二部分是使用实例来阐述findcontour的运用;第三部分是代码的解释。
一、论文大概思想
这篇论文我看了一天多,内容还是比较好理解。他主要介绍了两种算法,用来对数字二值图像进行拓扑分析。第一种算法是在确定二值图像边界的围绕关系,即确定外边界、孔边界以及他们的层次关系,由于这些边界和原图的区域具有一一对应关系(外边界对应像素值为1的连通区域,孔边界对应像素值为0的区域),因此我们就可以用边界来表示原图。第二种算法,是第一种算法的修改版本,本质一样,但是它只找最外面的边界。
也许你会问,这个算法怎么来确定外边界,孔边界以及他们的层级关系?他采用编码的思想,给不同的边界赋予不同的整数值,从而我们就可以确定它是什么边界以及层次关系。输入的二值图像即为0和1的图像,用f(i,j)表示图像的像素值。每次行扫描,遇到以下两种情况终止:
(1)f(i,j-1)=0,f(i,j)=1;//f(i,j)是外边界的起始点
(2)f(i,j)>=1,f(i,j+1)=0;//f(i,j)是孔边界的起始点
然后从起始点开始,标记边界上的像素。在这里分配一个唯一的标示符给新发现的边界,叫做NBD。初始时NBD=1,每次发现一个新边界加1。在这个过程中,遇到f(p,q)=1,f(p,q+1)=0时,将f(p,q)置为-NBD。什么意思呢?就是右边边界的终止点。假如一个外边界里有孔边界时,怎么推导呢?限于篇幅,你可以看论文的附录1。
原文:https://blog.csdn.net/helei001/article/details/23602449
函数cv::findContour是从二值图像中来计算轮廓的,它可以使用cv::Canny()函数处理的图像,因为这样的图像含有边缘像素;也可以使用cv::threshold()或者cv::adaptiveThreshold()处理后的图像,其边缘隐含在正负区域的交界处。
轮廓的层级结构
下左图所示findCountour()的基本功能,图的上部是一幅测试图像,其背景为白色,并含有数个彩色的的区域(标签A到E)。每块区域的外边界和内边界都各自组成轮廓,因此有9条轮廓。每条轮廓都有一组输出列表表示(右上图-轮廓参数),也可以选择生成一组层次表达(右下图-层次参数)。图中由findContours()所确定的轮廓被标记为cX或hX,其中c代表 “contour(轮廓)”,h代表“洞(hole)”,而X是一些数字。有些轮廓使用虚线表示的,他们表示白色区域(即非零区域)的外部边界。OpenCV和findContour()对这些外部边界和图中的点线,即内部边界或者是洞的外部边界,进行区分的。
如图的下半部分,OpenCV可以将找到的轮廓组织成轮廓树,表示其轮廓结构的包围关系。对于测试图像中的轮廓,我们将根节点处的轮廓称为c0,而“洞”h00和h01是其子节点。反过来这些子节点又会包含新的子节点以此类推。
表示这种树的方式有很多种,OpenCV中使用数组(尤其是vectors)来表示这种树,其中数组中的每个条目都代表一个特定的轮廓,每个条目包含一个由4个整数组成的集合(通常表示为cv :: Vec4i类型的元素,就像四通道数组中的条目一样)。对于每个节点来说,四个元素所表示的含义分别如下:
0号元素表示下一个轮廓(同一层级);
1号元素表示前一个轮廓(同一层级);
2号元素表示第一个子轮廓(下一层级);
3号元素表示父轮廓(上一层级)。
findContours()的具体调用有两种方式,函数原型:
void findContours(
InputOutputArray image, // 输入的8位单通道“二值”图像
OutputArrayOfArrays contours, // 包含points的vectors的vector
OutputArray hierarchy, // (可选) 拓扑信息
int mode, // 轮廓检索模式
int method, // 近似方法
cv::Point offset = Point() // (可选) 所有点的偏移
);
void findContours(
InputOutputArray image, // 输入的8位单通道“二值”图像
OutputArrayOfArrays contours, // 包含points的vectors的vector
int mode, // 轮廓检索模式
int method, // 近似方法
Point offset = Point() // (可选) 所有点的偏移
);
参数解释
第一个参数:image,图像必须为8-bit单通道图像,可以是灰度图,但更常用的是二值图像,图像中的非零像素将被视为1,0像素保留其像素值,故加载图像后会自动转换为二值图像。如果第四个参数mode为cv::RETR_CCOMP或cv::RETR_FLOODFILL,输入图像可以是32-bit整型图像(CV_32SC1)
第二个参数:contours,检测到的轮廓,每个轮廓都是以点向量的形式进行存储即使用point类型的vector表示 ,定义为“vector
第三个参数:hierarchy,定义为“vector
第四个参数:int型的mode,轮廓检索模式,将用何种方式来对轮廓进行提取,有四个可选的值::
取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓, 所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1。
取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
第五个参数:int型的method,定义轮廓的近似方法,即轮廓如何呈现的方法,有三种可选的方法:
取值一:CV_CHAIN_APPROX_NONE 获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1 ,保存物体边界上所有连续的轮廓点到contours向量内
取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours 向量内,拐点与拐点之间直线段上的信息点不予保留,压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
第六个参数:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值。
void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness=1, int lineType=8,
InputArray hierarchy=noArray(),
int maxLevel=INT_MAX, Point offset=Point() );
参数解释
image:输入输出图像,即要绘制轮廓的图像,Mat类型即可
contours:使用findContours检测到的所有轮廓数据,每个轮廓被保存成一个point向量的形式存储,point类型的vector
contourIdx:指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
color:绘制轮廓所用的颜色
thickness:绘制轮廓所用线条粗细度,如果值为负值,则在轮廓内部绘制 ,轮廓内部被填充
lineTpye:线条类型,轮廓线的邻域模式('4'邻域 或 '8'邻域)有默认值LINE_8,有如下可选类型
hierarchy:可选层次结构信息 , (从 findContours得到)
maxLevel= INT_MAX,//绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效
maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点用于绘制轮廓的最大等级
offset:可选轮廓便宜参数,用制定偏移量offset=(dx, dy)给出绘制轮廓的偏移量
添加一个滑动条,用于调节图像二值化的阈值,绘制并观察目标的轮廓。
#include
#include
#include
using namespace std;
using namespace cv;
int g_threshold = 50;
Mat gray_src,binary_src;
void trackbar(int, void*) {
threshold( gray_src,binary_src, g_threshold, 255, THRESH_BINARY);
imshow("binary_src", binary_src);
vector> contours;
findContours(binary_src, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
binary_src = Scalar::all(0);
drawContours(binary_src, contours, -1, Scalar::all(255));
imshow("contours", binary_src);
//使用Hu矩进行匹配
//double comres;
//comres = matchShapes(contours[0], contours[1], CV_CONTOURS_MATCH_I1, 0.0);
//cout<
结果:
from:https://blog.csdn.net/guduruyu/article/details/69220296
from: https://blog.csdn.net/keith_bb/article/details/70185209