vector<Rect>矩形框聚合拟合

groupRectangle函数实现矩形框聚合。原因:多尺度检测后,获取的矩形之间会存在重合、重叠和包含关系。因尺度缩放,可能导致同一个目标在多个尺度上被检测出来,故有必要进行融合。OpenCV中实现的融合有两种:1)按权重合并;2)使用Meanshift算法进行合并。

下面是简单的合并,其直接按照位置和大小关系进行合并。

其实现主要为:1)多所有矩形按照大小位置合并成不同的类别;

2)将同类别中的矩形合并成一个矩形,当不满足给出阈值条件时,矩形被舍弃,否则留下。

partition函数详解

template<typename _Tp, class _EqPredicate> int
partition( const std::vector<_Tp>& _vec, std::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;

    std::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++ )
    {
        int root = i;

        // find root
        // 寻找根节点,每次都是和每个节点对应的根节点比较,如果是单独的节点,根节点就是本身
        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];
                // root为根节点,root2为单独节点,将root2连接到root上,根节点不变
                if( rank > rank2 )
                    nodes[root2][PARENT] = root;
                // 当root和root2都为根节点,将root连接到root2,并将root2对应的rank加1,root2为根节点,root为单独点,将root连接
                // 到root2上。二者都将根节点更新为root2
                else
                {
                    nodes[root][PARENT] = root2;
                    nodes[root2][RANK] += rank == rank2;
                    root = root2;
                }
                // 根节点的parent必须小于0
                CV_Assert( nodes[root][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
        // 小于0,则已经统计过
        if( nodes[root][RANK] >= 0 )
            nodes[root][RANK] = ~nclasses++;
        // 每个根节点保存着类别ID的非值,其非值小于0
        labels[i] = ~nodes[root][RANK];
    }

    return nclasses;
}

} // cv
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps,
                     std::vector<int>* weights, std::vector<double>* levelWeights)
{
    if( groupThreshold <= 0 || rectList.empty() )
    {
        if( weights )
        {
            size_t i, sz = rectList.size();
            weights->resize(sz);
            for( i = 0; i < sz; i++ )
                (*weights)[i] = 1;
        }
        return;
    }

    std::vector<int> labels;
    // 调用partition函数,将所有的矩形框初步分为几类,其中labels为每个矩形框对应的类别编号,eps为判断两个矩形框是否属于
    // 同一类的控制参数。如果两个矩形框的四个相应顶点的差值的绝对值都在deta范围内,则认为属于同一类,否则是不同类。
    int nclasses = partition(rectList, labels, SimilarRects(eps));

    std::vector<Rect> rrects(nclasses);
    std::vector<int> rweights(nclasses, 0);
    std::vector<int> rejectLevels(nclasses, 0);
    std::vector<double> rejectWeights(nclasses, DBL_MIN);
    int i, j, nlabels = (int)labels.size();
    for( i = 0; i < nlabels; i++ )
    {
        int cls = labels[i];
        rrects[cls].x += rectList[i].x;
        rrects[cls].y += rectList[i].y;
        rrects[cls].width += rectList[i].width;
        rrects[cls].height += rectList[i].height;
        rweights[cls]++;
    }

    bool useDefaultWeights = false;

    if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
    {
        for( i = 0; i < nlabels; i++ )
        {
            int cls = labels[i];
            if( (*weights)[i] > rejectLevels[cls] )
            {
                rejectLevels[cls] = (*weights)[i];
                rejectWeights[cls] = (*levelWeights)[i];
            }
            else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
                rejectWeights[cls] = (*levelWeights)[i];
        }
    }
    else
        useDefaultWeights = true;
    // 计算每一类别的平均矩形框位置,即每一个类别最终对应一个矩形框
    for( i = 0; i < nclasses; i++ )
    {
        Rect r = rrects[i];
        float s = 1.f/rweights[i];
        rrects[i] = Rect(saturate_cast<int>(r.x*s),
             saturate_cast<int>(r.y*s),
             saturate_cast<int>(r.width*s),
             saturate_cast<int>(r.height*s));
    }

    rectList.clear();
    if( weights )
        weights->clear();
    if( levelWeights )
        levelWeights->clear();
    // 再次过滤上面分类中得到的所有矩形框
    for( i = 0; i < nclasses; i++ )
    {
        Rect r1 = rrects[i];
        int n1 = rweights[i];
        double w1 = rejectWeights[i];
        int l1 = rejectLevels[i];

        // filter out rectangles which don‘t have enough similar rectangles
        // 将每一类别中矩形框个数较少的类别过滤掉。
        if( n1 <= groupThreshold )
            continue;
        // filter out small face rectangles inside large rectangles
        // 将嵌在大矩形框内部的小矩形框过滤掉。最后剩下的矩形框为聚类的结果。
        for( j = 0; j < nclasses; j++ )
        {
            int n2 = rweights[j];

            if( j == i || n2 <= groupThreshold )
                continue;
            Rect r2 = rrects[j];

            int dx = saturate_cast<int>( r2.width * eps );
            int dy = saturate_cast<int>( r2.height * eps );

            if( i != j &&
                r1.x >= r2.x - dx &&
                r1.y >= r2.y - dy &&
                r1.x + r1.width <= r2.x + r2.width + dx &&
                r1.y + r1.height <= r2.y + r2.height + dy &&
                (n2 > std::max(3, n1) || n1 < 3) )
                break;
        }

        if( j == nclasses )
        {
            rectList.push_back(r1);
            if( weights )
                weights->push_back(useDefaultWeights ? n1 : l1);
            if( levelWeights )
                levelWeights->push_back(w1);
        }
    }
}

原文地址:https://www.cnblogs.com/lx17746071609/p/11549862.html

时间: 2024-10-15 01:20:22

vector<Rect>矩形框聚合拟合的相关文章

对opencv MeanShift 融合矩形框的改进

OPENCV 中的代码改进,当然要根据自己的实际情况来,OPENCV 中行人检测有两种矩形框的融合算法,这里只对meanshift 方法做改进 如果有更好的方法,希望可以跟我讲下. 对于去除重合部分,我也写了改进,看懂了可以加到自己程序中. 为什么要做局部MeanShift? 图1.全局MeanShift 如图所示:两幅图像距离较近且有多个矩形框,全局MeanShift融合后可能会造成这种结果 而如果用局部融合就能避免这种情况. /*-------------------------------

Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整(原理:底层SurfaceView+上层绘制ImageView)

Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整实现(原理:底层SurfaceView+上层绘制ImageView) 分类: Android开发 Android UI2013-05-23 15:04 1600人阅读 评论(1) 收藏 举报 目录(?)[+] http://blog.csdn.net/yanzi1225627/article/details/8580034 最近一直在审视以前做过的东西,关于android摄像头预览,预览界面上呈现矩形框,在

实现在矩形框中输入文字,可以显示剩余字数的功能

如下图: 要实现上面的功能,需要做到三点: 1.实现矩形框布局 思路就是矩形框作为整个布局的一个background,在drawable中创建一个shap.xml样式文件 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"     android:shape="r

GreenOpenPaint的实现(五)矩形框

想显示一副图片相对来说比较容易.但是想在SDI中显示一个可以拖拽的矩形框,并且在滚动和缩放下都不变形,是很困难的. MFC应该说在这个方面提供了强大的支持,但是也有一些是由于历史的原因需要去除的地方,特别是在缩放的这个地方还存在一些问题. 我认为关键是形成知识的框架,这样面对未来的需求就能够灵活应变. 这里,我结合GreenOpenPaint的具体实现进行总结. 我总结了一下,有几点: 1.肯定是要为这个矩形生成专门的类.这个类的实现,需要在DOC中,因为DOC就是用来管理所有的模型的:那么注意

Torch 两个矩形框重叠面积的计算 (IoU between tow bounding box)

Torch 两个矩形框重叠面积的计算 (IoU between tow bounding box) 1 function DecideOberlap(BBox_x1, BBox_y1, BBox_x2, BBox_y2, BBox_gt_x1, BBox_gt_y1, BBox_gt_x2, BBox_gt_y2) 2 3 x1 = BBox_x1; 4 y1 = BBox_y1; 5 width1 = BBox_x2 - BBox_x1; 6 height1 = BBox_y2 - BBox_

HDU 3265 Posters(线段树扫描线&#183;矩形框面积并)

题意  把一些矩形海报挖去一部分小矩形贴在指定位置  问最后海报覆盖的面积 一个矩形框可以分割成4个独立的小矩形  然后就能用扫描线求面积并了 #include <cstdio> #include <algorithm> using namespace std; const int N = 100005, M = N << 2; typedef long long ll; struct SLine { int x, y1, y2, flag; SLine() {}; S

CSS clip:rect矩形剪裁功能及一些应用介绍

CSS clip:rect矩形剪裁功能及一些应用介绍 by zhangxinxu from http://www.zhangxinxu.com 本文地址:http://www.zhangxinxu.com/wordpress/?p=1565 一.长话短说 CSS中有一个属性叫做clip,为修剪,剪裁之意.配合其属性关键字rect可以实现元素 的矩形裁剪效果.此属性安安稳稳地存在于CSS2.1中,且使用上基本上没有类似于max-height/display:table-cell等浏览器 的兼容性问

Skyline实现橡皮筋效果绘制矩形框

这种类似于框选的效果用的比较普遍,一般三维平台和GIS平台都提供了支持接口,可是Skyline就是这么傲娇! 思路是这样的:绘制出的矩形框应该是一直与屏幕边框平行的,也就是矩形框的实际旋转角度是等于摄像机的偏移角YAW值的,这样我们只要知道矩形的宽度,用矩形的两个对角点沿摄像机方向或者沿摄像机反方向延长一个宽度的距离就能得到矩形的另外两个点了.这个宽度利用两个对角点的距离和摄像机偏移角度很容易就能算出来,就不赘叙了. 最核心的代码就是正确计算矩形对角线与水平方向的夹角了: 1 //计算矩形对角点

用canvas实现鼠标拖动绘制矩形框

需要用到jCanvas插件和jQuery. jCanvas下载:https://raw.githubusercontent.com/caleb531/jcanvas/master/jcanvas.min.js 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>鼠标拖动绘制矩形框(canvas)</ti