首页 > 其他 > 详细

OpenCV中轮廓处理简介

时间:2020-07-03 09:24:49      阅读:87      评论:0      收藏:0      [点我收藏+]
一、OpenCV中的轮廓
        在OpenCV中,将 Canny 等边缘检测算法根据像素间的差异检测出轮廓边界的像素,作为一个整体来研究和分析,称之为轮廓。比如对于这副图片:
技术分享图片
图像的上半部分是一张白色背景上的测试图像,包含了一系列标记 A E的区域。寻找到的轮廓被标记为 cX 或 hX, 其中c 代表 “轮廓(contour)”,h 代表 “孔(hole)”(也可以理解为内轮廓)。
技术分享图片技术分享图片
同样,左图是原始图片,右图是寻找到的轮廓,它也采用了类似的标注方法。

二、函数调用细节
寻找轮廓的主要函数是 cv::findContours(),它的主要定义为:
void cv::findContours(
  cv::InputOutputArray    image,               // 输入图像,特别需要注意是二值图像     
  cv::OutputArrayOfArrays contours,        //输出结果
  cv::OutputArray         hierarchy,              // 层级结果
  int                     mode,                             //定义轮廓是如何提取
  int                     method,                          // 定义轮廓的寻找方法  
  cv::Point           offset = cv::Point()     // Offset every point
);
其中参数定义:
参数一:输入图像,8位单通道;
参数二:“an array of arrays”,一般采用“ an STL vector of STL vectors”,找到的轮廓、函数调用后的运算结果保存在这里;
参数三:hierarchy(层次,等级),可选输出向量。包含图像的拓扑信息。每个轮廓对应4元组,分别对应后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓。
参数四:flag 轮廓检索模式
参数五:flag 轮廓近似方法
其中,对于参数二它主要是以vector<cv::point>的形式保存寻找到的轮廓结果;
对于参数三一般表示为cv::Vec4i 型的元素,并且进一步按照以下结构来进行定义:

Index

Meaning

0

同级的下一条轮廓

1

同级的前一条轮廓

2

下级的第一个子节点

3

上级的父节点

通过这幅图能够很明显地看出层次关系:

技术分享图片

其对应的轮廓的相互关系为:


0  [ 7-11-1]
1  [-1-120]
2  [-1-131]
3  [-1-142]
4  [-1-153]
5  [ 6-1-14]
6  [-15-14]
7  [ 80-1-1]
8  [-17-1-1]

对于参数四轮廓检索模式:
技术分享图片
对于参数五轮廓近似方法:

      cv::CHAIN_APPROX_NONE

将轮廓编码中的所有点转换为点。 这一操作将会产生大量的点,每个点都将成为前一个点的8个邻点之一, 不会减少返回的点数,

       cv::CHAIN_APPROX_SIMPLE

压缩水平、垂直、斜的部分,只保留最后一个点。 在许多特殊情况下,这一操作将大大减少返回的点数。 极端例子是,对于一个沿着x-y x-y 轴方向的矩形(任意大小), 只会返回4个点。

       cv::CHAIN_APPROX_TC89_L1 or cv::CHAIN_APPROX_TC89_KCOS

使用Teh-Chin 链逼近算法中的一个。

三、重要函数
轮廓处理中经常遇到的任务是计算一些轮廓变化的概括特性这可能包括长度或其他一些反应轮廓整体大小的量度,包括以下函数:
cv::arcLength() 得到轮廓的长度
double  cv::arcLength(
  cv::InputArray  points,      // Array or vector of 2-dimensional points
  bool            closed       // If true, assume link from last to first vertex
);

第一个参数代表是轮廓,其形式可以是任何常见的轮廓表示方法(如标准模板库的点向量,或二通道数组)。 

第二个参数closed表示该轮廓是否是闭合的。 假如轮廓是闭合的,则参数points中的最后一个点到第一个的距离也算入总弧长中。

cv::minAreaRect()活动轮廓的最小外接矩形
cv::RotatedRect cv::minAreaRect( // Return rectangle bounding the points
  cv::InputArray  points,        // Array or vector of 2-dimensional points
);
其返回的是一个RotatedRect 结构。

cv::minEnclosingCircle()获得最小包围圆
void  cv::minEnclosingCircle(
   cv::InputArray points,        // Array or vector of 2-dimensional points
   cv::Point2f&   center,        // Result location of circle center
   float&         radius         // Result radius of circle
);
center radius 参数是需要获得的结果。

cv::convexHull()获得轮廓的凸包
void cv::convexHull(
  cv::InputArray  points,               // Array or vector of 2-d points
  cv::OutputArray hull,                 // Array of points or integer indices
  bool            clockwise    = false, // true=‘output points will be clockwise‘
  bool            returnPoints = true   // true=‘points in hull‘, else indices
);

pointPolygonTest 检测点是否落在多边形内
double cv::pointPolygonTest(   // Return distance to boundary (or just side)
  cv::InputArray contour,      // Array or vector of 2-dimensional points
  cv::Point2f    pt,           // Test point
  bool           measureDist   // true ‘return distance‘, else {0,+1,-1} only
);
当参数measureDist设为真,函数将返回该点距离最近的轮廓边缘的距离。若点在轮廓内,距离为0;若点在轮廓外,距离将是大于0的整数
基于pointPolygonTest,我们能够实现找已知轮廓最大内接圆”,具体来说,就是找到这样的结果:
技术分享图片

细节可以参考:https://github.com/opencv/opencv/pull/12206,这个实现已经被OpenCV收录。
四、快速连通区域分析

与轮廓分析紧密相关的另一种方法是连通区域分析.  采用阈值化等方法分割一张图像后,我们可以采用连通区域分析来有效地对返回图像逐张分离和处理。在以前,常用的方法是是先调用 cv::findContours() 函数(传入cv::RETR_CCOMP 标志),随后在得到的连通区域上循环调用 cv::drawContours() 

比如,我在GOCVHelper中这样进行了实现
    //寻找最大的轮廓
    VP FindBigestContour(Mat src){    
        int imax = 0//代表最大轮廓的序号
        int imaxcontour = -1//代表最大轮廓的大小
        std::vector<std::vector<Point>>contours;    
        findContours(src,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
        for (int i=0;i<contours.size();i++){
            int itmp =  contourArea(contours[i]);//这里采用的是轮廓大小
            if (imaxcontour < itmp ){
                imax = i;
                imaxcontour = itmp;
            }
        }
        return contours[imax];
    }
    //寻找并绘制出彩色联通区域
    vector<VP> connection2(Mat src,Mat& draw){    
        draw = Mat::zeros(src.rows,src.cols,CV_8UC3);
        vector<VP>contours;    
        findContours(src.clone(),contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
        //由于给大的区域着色会覆盖小的区域,所以首先进行排序操作
        //冒泡排序,由小到大排序
        VP vptmp;
        for(int i=1;i<contours.size();i++){
            for(int j=contours.size()-1;j>=i;j--){
                if (contourArea(contours[j]) < contourArea(contours[j-1]))
                {
                    vptmp = contours[j-1];
                    contours[j-1= contours[j];
                    contours[j] = vptmp;
                }
            }
        }
   }
从OpenCV3开始实现专门函数 cv::connectedComponents() 和函数 cv::connectedComponentsWithStats()寻找
int  cv::connectedComponents (
    cv::InputArrayn image,                // input 8-bit single-channel (binary)
    cv::OutputArray labels,               // output label map
    int             connectivity = 8,     // 4- or 8-connected components
    int             ltype        = CV_32S // Output label type (CV_32S or CV_16U)
    );
int  cv::connectedComponentsWithStats (
    cv::InputArrayn image,                // input 8-bit single-channel (binary)
    cv::OutputArray labels,               // output label map
    cv::OutputArray stats,  // Nx5 matrix (CV_32S) of statistics:[x0, y0, width0, height0, area0;... ; x(N-1), y(N-1), width(N-1),height(N-1), area(N-1)]
    cv::OutputArray centroids,            // Nx2 CV_64F matrix of centroids:[ cx0, cy0; ... ; cx(N-1), cy(N-1)]
    int             connectivity = 8,     // 4- or 8-connected components
    int             ltype        = CV_32S // Output label type (CV_32S or CV_16U)
    );
由于connectedComponentsWithStatsconnectedComponents的增强版,所以我们主要介绍connectedComponentsWithStats。
参数二、labels用来保存寻找到的“联通区域”,以0-N用来表示当前像素属于该张图片的第几个轮廓;
参数三、stats用来保存“联通区域”之间的关系,它是一个5XN的表格,可以直接使用ImageWtach打开。
技术分享图片
分别对应各个轮廓的x,y,width,height和area。注意0的区域标识的是background,所以出现了(0,0)。
参数四、centroids对应的是轮廓的重点
这张动图是label的结果,可以看到有数值的区域表示的就是“第几个轮廓”
技术分享图片

五、矩
        关于轮廓的另一个有用的特性是轮廓矩(contour moment),可以用来概括轮廓的总形状特性。一般来说轮廓矩代表了一条轮廓、一幅图像、一组点集的某些高级特征。

矩的数值定义如下式:

技术分享图片


在上式中,mp,q代表对象中所有像素的总和,其中每个像素x, y的像素值都乘以因子 xpyq. m00时,这个因子等于1。因此若图像为二值图(如,所有像素都等于0或者1),则 m00代表图像上所有值非零的区域。 当处理轮廓时,结果是轮廓的长度。

技术分享图片

m10m01分别除以 m00,能得到整个对象的平均x值和y值。

技术分享图片

技术分享图片

中心矩常用μp, q标注,定义如下

技术分享图片

其中:

技术分享图片技术分享图片

感谢阅读至此,希望有所帮助。







OpenCV中轮廓处理简介

原文:https://www.cnblogs.com/jsxyhelu/p/13228159.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!