矩形框融合

本文为本人看OpenCV源码及相关博客总结

1)行人检测完后后期,需要对矩形框进行融合,下面是本人看OpenCV源码总结的矩形融合原理。

第一步:将所有矩形框进行初步分类。分类原则是依据矩形框的相似性进行归类。

第二步:计算上步分类后的每一类别的平均矩形框位置,即每一个类别最终对应一个矩形框。

第三部:将第二步得到的矩形框再次进行过滤。过滤原理: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

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

下面是简单的合并,其直接按照位置和大小关系进行合并。其实现主要为:1)多所有矩形按照大小位置合并成不同的类别;2)将同类别中的矩形合并成一个矩形,当不满足给出阈值条件时,矩形被舍弃,否则留下。

//函数主要功能为对矩形框进行融合操作。
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);
        }
    }
}


  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 针对MVDnet的改进,我们可以利用激光雷达和毫米波雷达数据来拟合车辆或行人的高度信息,从而将二维矩形框转化为三维矩形框。具体实现步骤如下: 1. 获取激光雷达和毫米波雷达数据:在进行目标检测的同时,获取激光雷达和毫米波雷达数据,这些数据包含了目标物体的位置、速度、距离等信息。 2. 提取高度信息:通过对激光雷达和毫米波雷达数据的处理,提取出目标物体的高度信息,并将其与目标物体的位置、宽度、长度等信息进行融合。 3. 三维矩形框拟合:利用提取出来的目标物体的三维信息,进行三维矩形框的拟合。可以采用最小二乘法等算法,对目标物体的三维信息进行拟合,得到三维矩形框的参数。 4. 输出三维矩形框结果:将拟合出来的三维矩形框的参数输出,作为目标检测的结果。 通过这种方式,我们可以将MVDnet的目标检测结果从二维矩形框转化为三维矩形框,能够更加准确地描述目标物体的形状和大小,提高了目标检测的精度和可靠性。 ### 回答2: MVDnet是一种基于深度学习的目标检测方法,该方法可以通过分析图像数据提取出目标的位置和类别信息。然而,传统的目标检测方法只能提供目标的二维信息,而在实际应用中,获取目标的三维信息对于许多任务都是非常重要的。 为了解决这个问题,可以在MVDnet的基础上引入激光雷达数据和毫米波雷达数据,将其与图像数据结合使用,从而提供目标的高度信息,从而生成三维矩形框。激光雷达和毫米波雷达是常用的传感器,它们可以提供目标的距离和高度等信息,与图像数据相结合可以实现更加精准的目标检测和建模。 具体实施方法可以是,在训练阶段,将激光雷达和毫米波雷达数据与对应的图像数据一起输入MVDnet进行训练。这样,网络可以学习到目标在不同传感器下的特征表示,包括目标在高度上的差异。在测试阶段,使用训练好的网络,可以通过激光雷达和毫米波雷达数据提取出目标的高度信息,再结合图像数据的二维信息,就可以生成目标的三维矩形框。 该创新点的应用潜力非常广泛。首先,在自动驾驶领域,准确地获取目标的三维位置和形状信息,有助于实现更加安全和智能的驾驶体验。其次,在智能监控和安防领域,准确地追踪和识别目标,可以提高监控系统的准确性和反应速度。此外,在物流和仓储领域,三维目标检测可以帮助优化货物堆放和仓库管理等工作,提高工作效率。 总之,将原始的激光雷达数据和毫米波雷达数据引入MVDnet的创新点,可以实现将目标检测结果中的矩形框变成三维矩形框的目标。这一方法在自动驾驶、智能监控和物流仓储等领域具有广阔的应用前景。 ### 回答3: 在MVDnet的基础上引入原始的激光雷达数据和毫米波雷达数据,可以为检测结果添加高度信息,从而将原本的二维矩形框转化为三维矩形框。这一创新点有着重要的实际应用价值。 目前自动驾驶和智能交通系统中,无人驾驶车辆和交通监控系统等都需要对行人、车辆以及其他障碍物进行准确的三维位置识别和跟踪。传统的二维目标检测算法往往只能提供物体在水平平面上的位置信息,无法获取其高度信息,因此在复杂环境中容易产生误检或漏检问题。 通过将MVDnet与原始激光雷达和毫米波雷达数据相结合,可以实现对目标物体的三维信息获取。激光雷达利用激光束扫描周围环境,获取物体的空间坐标信息,而毫米波雷达则利用微波信号探测物体位置和速度。结合这两种传感器的数据,可以实现对物体的准确定位。 具体实现上,首先利用MVDnet对图像进行目标检测,得到二维矩形框的位置信息。然后,根据激光雷达和毫米波雷达数据,通过对目标物体周围进行立体点云处理,提取目标物体的三维位置信息。最后,将三维位置与二维矩形框结合,即可得到具有高度信息的三维矩形框。 这一创新不仅可以提高目标检测的准确性和鲁棒性,还有助于实现更精确的路径规划、碰撞避免等自动驾驶功能。同时,在智能交通系统中,通过获取行人和车辆的高度信息,可以进一步提升交通流量监控和交通事故预防的效果。 总而言之,通过在MVDnet的基础上引入原始激光雷达和毫米波雷达数据,并利用这些数据来拟合目标物体的高度信息,将检测结果转化为三维矩形框,可以为自动驾驶和智能交通系统带来更加精确和全面的目标识别和跟踪能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值