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

时间: 2024-09-20 04:21:29

OpenCV学习(28) 轮廓的相关文章

OpenCV学习(30) 轮廓defects

     上一篇教程中,我们学习了如何计算轮廓的凸包,其实对一个轮廓而言,可能它的凸包和它本身是重合的,也有可能不是重合的.比如下面左边图像的轮廓本身就是凸包,而右边图像的轮廓则不是.我们可以通过函数bool isContourConvex(InputArray contour),来判定一个轮廓是否是凸包,是的话返回true,否则false[注意测试的轮廓必须是简单轮廓,没有自交叉之类的].       对一个非凸包的轮廓而言,它包括一系列的凹陷区域,这些区域称作defect,比如下面手轮廓中,

OpenCV学习(33) 轮廓的特征矩Moment

      在OpenCV中,可以很方便的计算多边形区域的3阶特征矩,opencv中的矩主要包括以下几种:空间矩,中心矩和中心归一化矩. class Moments { public: ...... // 空间矩 double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; // 中心矩 double mu20, mu11, mu02, mu30, mu21, mu12, mu03; // 中心归一化矩 double nu20, nu11, n

OpenCV 查找图像轮廓

OpenCV 查找图像轮廓 目的 我们将学习: 学会使用 OpenCV 函数 findContours 学会使用 OpenCV 函数 drawContours 原理 代码 下面是本示例的代码,可以从这里 下载 #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> #

指针-opencv学习灰度图锐化的两个函数的差别不理解

问题描述 opencv学习灰度图锐化的两个函数的差别不理解 下面是一个灰度图锐化的函数,我有两种方式实现,方式1,和方式2,居然得到的结果不一样,图片数据也不一样,请高手看一下,可能是C语言的知识掌握的不好. void my_sharpen(const cv::Mat &image, cv::Mat &result) { result.create (image.size(), image.type ()); for(int j=1; j<image.rows-1; j++) { u

库函数-opencv中图像轮廓矢量化算法

问题描述 opencv中图像轮廓矢量化算法 opencv中图像轮廓矢量化算法,有直接矢量化的库函数么?急求答案! 解决方案 说的有点含糊,如果你是要opencv中的代码,你可以直接抠出来用.

(转) OpenCV学习笔记大集锦 与 图像视觉博客资源2之MIT斯坦福CMU

      首页 视界智尚 算法技术 每日技术 来打我呀 注册     OpenCV学习笔记大集锦 整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的.如果有好的资源,也欢迎介绍和分享. 1:OpenCV学习笔记 作者:CSDN数量:55篇博文网址:http://blog.csdn.net/column/details/opencv-manual.html 2:部分OpenCV的函数解读和原理解读 作者:梦想腾飞数量:20篇博文网址:http:/

OpenCV学习(32) 求轮廓的包围盒

      在OpenCV中,能够很方便的求轮廓包围盒.包括矩形,圆形,椭圆形以及倾斜的矩形(包围面积最小)集中包围盒.用到的四个函数是: Rect boundingRect(InputArray points) void minEnclosingCircle(InputArray points, Point2f& center, float& radius) RotatedRect minAreaRect(InputArray points) RotatedRect fitEllipse

OpenCV学习(34) 点到轮廓的距离

      在OpenCV中,可以很方便的计算一个像素点到轮廓的距离,计算距离的函数为: double pointPolygonTest(InputArray contour, Point2f pt, bool measureDist) Parameters: contour – 输入参数轮廓. pt – 测试的点. measureDist – 如果为false的话,则函数计算符号,在轮廓外部在为-1,在轮廓内为1,在轮廓上,则为0.如果为ture,则计算实际的像素符号距离,在轮廓外的点像素距离

OpenCV学习(9) 分水岭算法(3)

本教程我学习一下opencv中分水岭算法的具体实现方式. 原始图像和Mark图像,它们的大小都是32*32,分水岭算法的结果是得到两个连通域的轮廓图. 原始图像:(原始图像必须是3通道图像) Mark图像: 结果图像:       初始的mark图像数据如下,黄色的部分为我们的第一个mark区域,值为255,第二个区域为褐红色的区域,值为128,第三个绿色的区域,值为64.   opencv分水岭算法描述如下: 初始化mark矩阵,生成最初的注水区域. 1.设置mark图像的边框值为-1 2.