\tutorial_code\ShapeDescriptors


打算酷暑过后再去武汉






【21】轮廓

findContours_demo.cpp
generalContours_demo1.cpp
generalContours_demo2.cpp
moments_demo.cpp
pointPolygonTest_demo.cpp



void findContours(const Mat& image, vector<vector<Point> >& contours, vector<Vec4i>& hierarchy, int mode, int method, Point offset=Point())

void findContours(const Mat& image, vector<vector<Point> >& contours, int mode, int method, Point offset=Point())

输入图像image必须为一个2值单通道图像

contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示

hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。

mode表示轮廓的检索模式

CV_RETR_EXTERNAL表示只检测外轮廓

CV_RETR_LIST检测的轮廓不建立等级关系

CV_RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。

CV_RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo


method为轮廓的近似办法

CV_CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1

CV_CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法


offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。


findContours经常与drawContours配合使用,用来将轮廓绘制出来。

void drawContours(Mat& image, const vector<vector<Point> >& contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, const vector<Vec4i>& hierarchy=vector<Vec4i>(), int maxLevel=INT_MAX, Point offset=Point())

其中
第一个参数image表示目标图像,
第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,
第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
第四个参数color为轮廓的颜色,
第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
第六个参数lineType为线型,
第七个参数为轮廓结构信息,
第八个参数为maxLevel


findContours后会对输入的2值图像改变,所以如果不想改变该2值图像,需创建新mat来存放,findContours后的轮廓信息contours可能过于复杂不平滑,可以用approxPolyDP函数对该多边形曲线做适当近似

contourArea函数可以得到当前轮廓包含区域的大小,方便轮廓的筛选





        /// Find contours
        findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

        /// Draw contours
        Mat Contoursdrawing = Mat::zeros( canny_output.size(), CV_8UC3 );
        for( int i = 0; i<(int) contours.size(); i++ )
        {
                Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                drawContours( Contoursdrawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
        }


Contours



        /// Approximate contours to polygons + get bounding rects and circles
        vector<vector<Point> > contours_poly( contours.size() );

        /// Draw polygonal contour + bonding rects + circles
        Mat approxPolyDPdrawing = Mat::zeros( canny_output.size(), CV_8UC3 );

        for( int i = 0; i< (int) contours.size(); i++ )
        {
                Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                drawContours( approxPolyDPdrawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
        }


approxPolyDP



  /// get bounding rects and circles
        vector<Rect> boundRect( contours.size() );
        vector<Point2f>center( contours.size() );
        vector<float>radius( contours.size() );

        for( int i = 0; i < (int) contours.size(); i++ )
        {
                approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
                boundRect[i] = boundingRect( Mat(contours_poly[i]) );
                minEnclosingCircle( contours_poly[i], center[i], radius[i] );
        }

        /// Draw polygonal contour + bonding rects + circles
        Mat REdrawing = Mat::zeros( canny_output.size(), CV_8UC3 );

                for( int i = 0; i< (int) contours.size(); i++ )
        {
                Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                rectangle( REdrawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
                circle( REdrawing, center[i], (int)radius[i], color, 2, 8, 0 );
        }


rectangles and ellipses




        /// Find the rotated rectangles and ellipses for each contour
        vector<RotatedRect> minRect( contours.size() );
        vector<RotatedRect> minEllipse( contours.size() );

        for( int i = 0; i < (int) contours.size(); i++ )
        {
                minRect[i] = minAreaRect( Mat(contours[i]) );
                if( contours[i].size() > 5 )
                {
                        minEllipse[i] = fitEllipse( Mat(contours[i]) );
                }
        }

        /// Draw contours + rotated rects + ellipses
        Mat rREdrawing = Mat::zeros( canny_output.size(), CV_8UC3 );
        for( int i = 0; i< (int) contours.size(); i++ )
        {
                Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                // ellipse
                ellipse( rREdrawing, minEllipse[i], color, 2, 8 );
                // rotated rectangle
                Point2f rect_points[4]; minRect[i].points( rect_points );
                for( int j = 0; j < 4; j++ )
                        line( rREdrawing, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );
        }


        /// Find the rotated rectangles and ellipses for each contour
        vector<RotatedRect> minRect( contours.size() );
        vector<RotatedRect> minEllipse( contours.size() );

        for( int i = 0; i < (int) contours.size(); i++ )
        {
                minRect[i] = minAreaRect( Mat(contours[i]) );
                if( contours[i].size() > 5 )
                {
                        minEllipse[i] = fitEllipse( Mat(contours[i]) );
                }
        }

        /// Draw contours + rotated rects + ellipses
        Mat rREdrawing = Mat::zeros( canny_output.size(), CV_8UC3 );
        for( int i = 0; i< (int) contours.size(); i++ )
        {
                Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                // ellipse
                ellipse( rREdrawing, minEllipse[i], color, 2, 8 );
                // rotated rectangle
                Point2f rect_points[4]; minRect[i].points( rect_points );
                for( int j = 0; j < 4; j++ )
                        line( rREdrawing, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );
        }


rotated rectangles and ellipses



 /// Find the convex hull object for each contour
        vector<vector<Point> >hull( contours.size() );
        for( int i = 0; i < contours.size(); i++ )
        { convexHull( Mat(contours[i]), hull[i], false ); }

        /// Draw hull results
        Mat Hulldrawing = Mat::zeros( canny_output.size(), CV_8UC3 );
        for( int i = 0; i< contours.size(); i++ )
        {
                Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                drawContours( Hulldrawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
        }

Hull





 /// Get the moments
  vector<Moments> mu(contours.size() );
  for( int i = 0; i < (int) contours.size(); i++ )
     { mu[i] = moments( contours[i], false ); }

  /// Get the mass centers:
  vector<Point2f> mc( contours.size() );
  for( int i = 0; i < (int) contours.size(); i++ )
     { mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }

  /// Draw contours
  Mat Movementdrawing = Mat::zeros( canny_output.size(), CV_8UC3 );

  /// Calculate the area with the moments 00 and compare with the result of the OpenCV function
  printf("\t Info: Area and Contour Length \n");
  for( int i = 0; i< (int) contours.size(); i++ )
     {
       printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) );
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       circle( Movementdrawing, mc[i], 4, color, -1, 8, 0 );
     }
  /// Show in a window
  namedWindow( "Movement", CV_WINDOW_AUTOSIZE );
  imshow( "Movement", Movementdrawing );

使用OpenCV函数 moments 计算图像所有的矩(最高到3阶)
使用OpenCV函数 contourArea 来计算轮廓面积
使用OpenCV函数 arcLength 来计算轮廓或曲线长度






contourArea and arcLength





        /// Calculate the distances to the contour
        Mat raw_dist( dsrc.size(), CV_32FC1 );

        for( int j = 0; j < dsrc.rows; j++ )
        {
                for( int i = 0; i < dsrc.cols; i++ )
                {
                        raw_dist.at<float>(j,i) = pointPolygonTest( contours[0], Point2f(i,j), true );
                }
        }

        double minVal; double maxVal;
        minMaxLoc( raw_dist, &minVal, &maxVal, 0, 0, Mat() );
        minVal = abs(minVal); maxVal = abs(maxVal);

        /// Depicting the distances graphically
        Mat distancedrawing = Mat::zeros( src_copy.size(), CV_8UC3 );

        for( int j = 0; j < dsrc.rows; j++ )
        {
                for( int i = 0; i < dsrc.cols; i++ )
                {
                        if( raw_dist.at<float>(j,i) < 0 )
                        {
                                distancedrawing.at<Vec3b>(j,i)[0] = 255 - (int) abs(raw_dist.at<float>(j,i))*255/minVal;
                        }
                        else if( raw_dist.at<float>(j,i) > 0 )
                        {
                                distancedrawing.at<Vec3b>(j,i)[2] = 255 - (int) raw_dist.at<float>(j,i)*255/maxVal;
                        }
                        else
                        {
                                distancedrawing.at<Vec3b>(j,i)[0] = 255; distancedrawing.at<Vec3b>(j,i)[1] = 255; distancedrawing.at<Vec3b>(j,i)[2] = 255;
                        }
                }
        }

pointPolygonTest




得到轮廓的外包络矩形,使用函数boundingRect,
如果想得到旋转的外包络矩形,使用函数minAreaRect,返回值为RotatedRect;
也可以得到轮廓的外包络圆,对应的函数为minEnclosingCircle;
想得到轮廓的外包络椭圆,对应的函数为fitEllipse,返回值也是RotatedRect,可以用ellipse函数画出对应的椭圆

得到了复杂轮廓往往不适合特征的检测,这里再介绍一个点集凸包络的提取函数convexHull,输入参数就可以是contours组中的一个轮廓,返回外凸包络的点集


如果想根据多边形的轮廓信息得到多边形的多阶矩,可以使用类moments,这个类可以得到多边形和光栅形状的3阶以内的所有矩,类内有变量m00,m10,m01,m20,m11,m02,m30,m21,m12,m03,比如多边形的质心为 x = m10 / m00,y = m01 / m00。

如果想获得一点与多边形封闭轮廓的信息,可以调用pointPolygonTest函数,这个函数返回值为该点距离轮廓最近边界的距离,为正值为在轮廓内部,负值为在轮廓外部,0表示在边界上。


请参阅:
http://blog.csdn.net/yang_xian521/article/details/6922798#





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值