OpenCV学习(28) 轮廓
OpenCV中可以方便的在一副图像中检测到轮廓,并把这些轮廓画出来。主要用到两个函数:一个是findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);另一个是drawContours( cnt_img, contours, idx, color, 1, 8, hierarchy );
int main( int argc, char**)
{
Mat img = Mat::zeros(w, w, CV_8UC1); //画6个卡通头像
for( int i = 0; i < 6; i++ )
{
int dx = (i%2)*250 - 30;
int dy = (i/2)*150;
const Scalar white = Scalar(255);
const Scalar black = Scalar(0); if( i == 0 )
{
//给第一个卡通画上11根头发
for( int j = 0; j <= 10; j++ )
{
double angle = (j+5)*CV_PI/21;
line(img, Point(cvRound(dx+100+j*10-80*cos(angle)),
cvRound(dy+100-90*sin(angle))),
Point(cvRound(dx+100+j*10-30*cos(angle)),
cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);
}
} ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 ); }
//显示所画的图像
namedWindow( "image", 1 );
imshow( "image", img );
//抽取轮廓
vector<vector<Point> > contours0;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); contours.resize(contours0.size());
//多边形近似,第三个参数是精度,表示近似曲线和原始曲线之间的距离
for( size_t k = 0; k < contours0.size(); k++ )
approxPolyDP(Mat(contours0[k]), contours[k], 3, true); namedWindow( "contours", 1 ); Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
int idx = 0;
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img, contours, idx, color, 1, 8, hierarchy );
}
imshow("contours", cnt_img); namedWindow( "contours1", 1 ); Mat cnt_img1 = Mat::zeros(w, w, CV_8UC3);
idx = 0;
//for( ; idx >= 0; idx = hierarchy[idx][0] )
// {
// Scalar color( rand()&255, rand()&255, rand()&255 );
// drawContours( cnt_img1, contours, idx, color,1, 1, hierarchy, 2 );
// }
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img1, contours, -1, color,1, 1 );
imshow("contours1", cnt_img1); while(1)
waitKey(); return 0;
}
上面的代码会首先画6个卡通头像,然后检测卡通头像中的轮廓并画出来。



其中抽取轮廓的代码为:
//抽取轮廓
vector<vector<Point> > contours0;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
findContours函数原型为:
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, intmethod, Point offset=Point())
- image – 8bit的单通道图像。
- contours – 检测到的轮廓,每个轮廓都是一系列的点组成。 通常用vector<vector<Point> > 定义轮廓。
- hierarchy – 定义为vector<Vec4i> hierarchy; 对第i个轮廓,元素hierarchy[i][0] ,hiearchy[i][1] , hiearchy[i][2] , 和 hiearchy[i][3]分别表示同一层级前一个轮廓和后一个轮廓索引,第一个孩子轮廓和父轮廓索引。如果值为-1,则表示该轮廓不存在相应的关系轮廓。
- 第四个参数为mode – 模式:
CV_RETR_EXTERNAL 仅检测外部轮廓,它会设置hierarchy[i][2]=hierarchy[i][3]=-1 。
CV_RETR_LIST 没有任何层级的检测所有轮廓,这样轮廓之间就失去了拓扑语义,全成了平级关系。
- CV_RETR_CCOMP 检测所有轮廓,然后组织成2级模式,顶层是外部轮廓,第二级是内部洞的轮廓。如果里面还有嵌套轮廓,会重新成为2级关系。

- CV_RETR_TREE 检索全层级嵌套轮廓,我们程序中就是使用这个设置。
- method – 近似求轮廓的方法:
CV_CHAIN_APPROX_NONE 连续存储所有的轮廓点,任何两个相邻的点都是水平、垂直或者斜相邻的。也就是说 max(abs(x1-x2),abs(y2-y1))==1.
- CV_CHAIN_APPROX_SIMPLE 压缩存储,对于水平,垂直或者斜向的线段,只会保存端点。比如一个四边形,只会存储四个顶点。
- CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS
- 参考paper:Teh, C.H. and Chin, R.T., On the Detection of Dominant Points on Digital Curve. PAMI 11 8, pp 859-872 (1989)
- offset – 每个轮廓点被偏移的距离。当在ROI区域检测轮廓时,这个是有用的,因为一般需要在原图中分析和画轮廓。
函数findContours检测轮廓的算法来自于paper:Suzuki, S. and Abe, K., Topological Structural Analysis of Digitized Binary Images by Border Following. CVGIP 30 1, pp 32-46 (1985)
在代码中,我们接着用了一个近似多边形函数,代码如下:
contours.resize(contours0.size());
//多边形近似,第三个参数是精度,表示近似曲线和原始曲线之间的距离
for( size_t k = 0; k < contours0.size(); k++ )
approxPolyDP(Mat(contours0[k]), contours[k], 3, true);
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
curve – 轮廓点集表示的曲线。通常用vector表示。
approxCurve – 输出的近似多边形曲线。
- epsilon – 近似精度,表示原始曲线和近似曲线之间的最大距离,比如代码中3。
- closed – 曲线是否是闭合曲线。
多边形近似用的算法是:http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
如果增大精度,则轮廓曲线越不光滑,比如我们设置epsilon为6,则得到下面的轮廓图:

最后我们要用drawContours把轮廓画出来:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, intthickness=1, int lineType=LINE_8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
Parameters:
- image – 目地图像,把轮廓画在这幅图像上,最好大小和原始图像相等。
- contours – 通过findContours得到的所有轮廓,通常用vector<vector<Point> >表示。
- contourIdx – 要画的轮廓的索引,通常它的大小在0到contours.size之间,如果为-1,则会画出所有轮廓。
- color – 轮廓的颜色.
- thickness – 线的宽度,如果为-1,则为填充模式。
- lineType – 线的类型,比如实线,虚线等待。
- hierarchy – 就是findContours函数得到的轮廓层级信息,它和最后一个参数maxLevel相结合,用来指定画那些轮廓。如果maxLevel为0,则画指定的轮廓,如果为1,则会指定的轮廓和它的一级子轮廓,如果maxLevel为2,则会该轮廓和所有的嵌套子轮廓。
- offset – 偏移选项,就是画轮廓时候从原始位置偏移一个距离来画轮廓。
比如下面的代码:
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img1, contours, idx, color, -1, 1, hierarchy, 2 );
}
则画的轮廓为:

for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img1, contours, idx, color, 1, 1, hierarchy, 0 );
}
输出轮廓为:

for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img1, contours, idx, color, 1, 1, hierarchy, 1 );
}
输出轮廓为:

for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img1, contours, idx, color, 1, 1, hierarchy, 2 );
}
输出轮廓为:

Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( cnt_img1, contours, -1, color,1, 1 );
输出轮廓为下图,第三个参数为-1,不用循环,就可以画出所有轮廓。

程序代码:FirstOpenCV23
OpenCV学习(28) 轮廓的更多相关文章
- OpenCV学习(30) 轮廓defects
上一篇教程中,我们学习了如何计算轮廓的凸包,其实对一个轮廓而言,可能它的凸包和它本身是重合的,也有可能不是重合的.比如下面左边图像的轮廓本身就是凸包,而右边图像的轮廓则不是.我们可以通过函数bool ...
- OpenCV学习(33) 轮廓的特征矩Moment
在OpenCV中,可以很方便的计算多边形区域的3阶特征矩,opencv中的矩主要包括以下几种:空间矩,中心矩和中心归一化矩. class Moments { public: ...... // 空间矩 ...
- opencv学习笔记(二)寻找轮廓
opencv学习笔记(二)寻找轮廓 opencv中使用findContours函数来查找轮廓,这个函数的原型为: void findContours(InputOutputArray image, O ...
- OpenCV 学习笔记03 边界框、最小矩形区域和最小闭圆的轮廓
本节代码使用的opencv-python 4.0.1,numpy 1.15.4 + mkl 使用图片为 Mjolnir_Round_Car_Magnet_300x300.jpg 代码如下: impor ...
- OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波
http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...
- OpenCV 学习笔记03 凸包convexHull、道格拉斯-普克算法Douglas-Peucker algorithm、approxPloyDP 函数
凸形状内部的任意两点的连线都应该在形状里面. 1 道格拉斯-普克算法 Douglas-Peucker algorithm 这个算法在其他文章中讲述的非常详细,此处就详细撰述. 下图是引用维基百科的.ε ...
- OpenCV 学习笔记03 boundingRect、minAreaRect、minEnclosingCircle、boxPoints、int0、circle、rectangle函数的用法
函数中的代码是部分代码,详细代码在最后 1 cv2.boundingRect 作用:矩形边框(boundingRect),用于计算图像一系列点的外部矩形边界. cv2.boundingRect(arr ...
- OpenCV 学习笔记03 findContours函数
opencv-python 4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...
- OpenCV 学习笔记 02 使用opencv处理图像
1 不同色彩空间的转换 opencv 中有数百种关于不同色彩空间的转换方法,但常用的有三种色彩空间:灰度.BRG.HSV(Hue-Saturation-Value) 灰度 - 灰度色彩空间是通过去除彩 ...
随机推荐
- Bootstrap--响应式图片轮播
<div class="row"> <div class="span12"> <section id="carousel ...
- pandas实战——对星巴克数据的分析
一.实验对象 实验对象为星巴克在全球的门店数据,我们可以使用pandas对其进行简单的分析,如分析每个国家星巴克的数量,根据门店数量对国家进行排序等. 二.数据分析 1.读取数据并获取数据行列数 首先 ...
- Python学习之文件操作
Python 文件打开方式 文件打开方法:open(name[,mode[buf]]) name:文件路径mode:打开方式buf:缓冲buffering大小 f = open('test.txt', ...
- mysql正则表达式,实现多个字段匹配多个like模糊查询
现在有这么一个需求 一个questions表,字段有题目(TestSubject),选项(AnswerA,AnswerB,AnswerC,AnswerD,AnswerE) 要求字段不包含png,jpg ...
- Django Q对象
使用Q 对象进行复杂的查询¶ filter() 等方法中的关键字参数查询都是一起进行“AND” 的. 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象. Q 对象 (django.db ...
- 如何破解安卓App
韩梦飞沙 yue31313 韩亚飞 han_meng_fei_sha 313134555@qq.com 如何破解安卓App
- wpf企业应用之带选项框的TreeView
wpf里面实现层次绑定主要使用HierarchicalDataTemplate,这里主要谈一谈带checkbox的treeview,具体效果见 wpf企业级开发中的几种常见业务场景. 先来看一下我的控 ...
- 家谱(gen)
家谱(gen) 时间限制 2S [问题描述] 现代的人对于本家族血统越来越感兴趣,现在给出充足的父子关系,请你编写程序找到某个人的最早的祖先. [输入格式]gen.in 输入文件由多行组成, ...
- [BZOJ4820][SDOI2017]硬币游戏(高斯消元+KMP)
比较神的一道题,正解比较难以理解. 首先不难得出一个(nm)^3的算法,对所有串建AC自动机,将在每个点停止的概率作为未知数做高斯消元即可. 可以证明,AC自动机上所有不是模式串终止节点的点可以看成一 ...
- ST-PUZZLE-2.0(一个益智游戏)
注:未经博主允许不得转载. 原文链接:http://www.cnblogs.com/Blog-of-Eden/p/9060300.html 和 https://i-m-eden.github.io/2 ...