groupRectangles函数详解

void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights)

1. 调用partition,将所有的矩形框初步分为几类,其中出参labels为每个矩形框对应的类别编号,入参eps为判断两个矩形框是否属于同一类的控制参数。如果两个矩形框的四个相应顶点的差值的绝对值都在deta范围内,则认为属于同一类,否则是不同类。

其中deta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;

返回值nclasses为类别数目。

int nclasses = partition(rectList, labels, SimilarRects(eps));

2. 计算每一类别的平均矩形框位置,即每一个类别最终对应一个矩形框。

3. 将2中得到的所有矩形框再次过滤

(1)将每一类别中矩形框个数较少的类别过滤掉。每一类中的矩形框个数是由可以由partition分类得出的,即可以得出每一类有多少个矩形框。而步骤2仅仅是将这些个矩形框求其位置平均值。过滤的个数阈值为入参groupThreshold,只有当某一个类别中的矩形框个数大于此阈值,才保留该类别。

(2)将嵌在大矩形框内部的小矩形框过滤掉。最后剩下的矩形框即为聚类的结果。

 

下面介绍一下partition函数

template<typename _Tp, class _EqPredicate> int

partition( const vector<_Tp>& _vec, vector<int>& labels,

           _EqPredicate predicate=_EqPredicate())

{

    int i, j, N = (int)_vec.size();

    const _Tp* vec = &_vec[0];

    const int PARENT=0;

    const int RANK=1;

    vector<int> _nodes(N*2);

    int (*nodes)[2] = (int(*)[2])&_nodes[0];

 

// The first O(N) pass: create N single-vertex trees

// nodes[i][PARENT]=-1表示无父节点,所有节点初始化为单独的节点

    for(i = 0; i < N; i++)

    {

        nodes[i][PARENT]=-1;

        nodes[i][RANK] = 0;

}

 

// The main O(N^2) pass: merge connected components

//每一个节点都和其他所有的节点比较,看是否是属于同一类

//属于同一类的判断:predicate(vec[i], vec[j]), predicate为传入的SimilarRects

   //SimilarRects判断两个矩形框的四个相应顶点的差值的绝对值都在deta范围内,

//则认为属于同一类,否则是不同类

//两层for循环和后面的压缩策略保证了最终形成以很多类,每一类以根节点为中心,、、//其余该类的节点的父坐标都指向根节点

    for( i = 0; i < N; i++ )

{

// find root寻找根节点,每次都是和每个节点对应的根节点比较

        //如果是单独的节点,根节点就是本身

        int root = i;       

        while( nodes[root][PARENT] >= 0 )

            root = nodes[root][PARENT];

 

        for( j = 0; j < N; j++ )

        {

            //同一节点或者两个节点的矩形框差距大则不连接

            if( i == j || !predicate(vec[i], vec[j]))

                continue;

 

    // 寻找可以归为同一类节点的根节点,每次都是和对应的根节点先连接,

    //即比较两个节点的矩形框,连接时,使用两个节点对应的两个根节点

            //这样保证了已经连接在同一类的不再连接,不同类的也容易连接

            int root2 = j;

            while( nodes[root2][PARENT] >= 0 )

                root2 = nodes[root2][PARENT];

 

            if( root2 != root )  //保证已经连接在同一类的不再连接

            {

                // unite both trees

//rank表示级别,根节点rank大为0,普通点rank为0,并且根节点的rank

//随着连接同级根节点的次数的增多而增大

                int rank = nodes[root][RANK], rank2 = nodes[root2][RANK];

                if( rank > rank2 ) //root为根节点,root2是单独点,将root2连接到root上,根节点不变

                    nodes[root2][PARENT] = root;

                else   //当root和root2都为根节点时,将root连接到root2上,并将root2对应的rank加1;当root2为根节点,root是单独点,将root连接到root2上。二者都将根节点更新为root2

                {

                    nodes[root][PARENT] = root2;

                    nodes[root2][RANK] += rank == rank2;

                    root = root2;

                }

                assert( nodes[root][PARENT] < 0 );//根节点的parent必须小于0

 

                int k = j, parent;

 

                // compress the path from node2 to root

                //下一级节点通过它的根节点连接到上一级根节点时,直接将下一级节点和根节点都连接到上级的根节点,如果是单独的节点连接到某个根节点,循环不改变任何值

                while( (parent = nodes[k][PARENT]) >= 0 )

                {

                    nodes[k][PARENT] = root;

                    k = parent;

                }

 

                // compress the path from node to root

                //同一级节点通过它的根节点连接到同级的根节点时,直接将该节点和根节点都连接到同级的根节点,如果是单独的节点连接到某个根节点,循环不改变任何值

                k = i;

                while( (parent = nodes[k][PARENT]) >= 0 )

                {

                    nodes[k][PARENT] = root;

                    k = parent;

                }

            }

        }

    }

 

// Final O(N) pass: enumerate classes

 

    labels.resize(N);

    int nclasses = 0;//总分类数

    for( i = 0; i < N; i++ )

    {

        int root = i;

        while( nodes[root][PARENT] >= 0 )

            root = nodes[root][PARENT];

        // re-use the rank as the class label

        if( nodes[root][RANK] >= 0 )//小于0,则已经统计过了

            nodes[root][RANK] = ~nclasses++;

        labels[i] = ~nodes[root][RANK]; //每个根节点保存着类别ID的非值,其非值小于0

    }

 

    return nclasses;

}

 

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值