zoukankan      html  css  js  c++  java
  • 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个卡通头像,然后检测卡通头像中的轮廓并画出来。

    imageimageimage

    其中抽取轮廓的代码为:

    //抽取轮廓
    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级关系。
      • image
      • 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,则得到下面的轮廓图:

    image

    最后我们要用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 );
        }

    则画的轮廓为:

    image

    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 );
    }

    输出轮廓为:

    image

    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 );
    }

    输出轮廓为:

    image

    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 );
    }

    输出轮廓为:

    image

    Scalar color( rand()&255, rand()&255, rand()&255 );
    drawContours( cnt_img1, contours, -1, color,1, 1 );

    输出轮廓为下图,第三个参数为-1,不用循环,就可以画出所有轮廓。

    image

    程序代码:FirstOpenCV23

  • 相关阅读:
    MySQL 中随机抽样:order by rand limit 的替代方案
    mysql下distinct和group by区别对比
    MVC中实现多按钮提交(转)
    js的逻辑 OR 运算符- ||
    js 实现键盘记录 兼容FireFox和IE
    jquery扩展
    sp_executesql的用法
    MVC中,视图的Layout使用
    MVC4的过滤器
    MVC中的Repository模式
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/3416963.html
Copyright © 2011-2022 走看看