realkate1

导航

OpenCV函数解读之groupRectangles

不管新版本的CascadeClassifier,还是老版本的HAAR检测函数cvHaarDetectObjects,都使用了groupRectangles函数进行窗口的组合,其函数原型有以下几个:
CV_EXPORTS void groupRectangles(CV_OUT CV_IN_OUT vector<Rect>& rectList, int groupThreshold, double eps=0.2);
CV_EXPORTS_W void groupRectangles(CV_OUT CV_IN_OUT vector<Rect>& rectList, CV_OUT vector<int>& weights, int groupThreshold, double eps=0.2);
CV_EXPORTS void groupRectangles( vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights );
CV_EXPORTS void groupRectangles(vector<Rect>& rectList, vector<int>& rejectLevels,
                                vector<double>& levelWeights, int groupThreshold, double eps=0.2);
 
CV_EXPORTS void groupRectangles_meanshift(vector<Rect>& rectList, vector<double>& foundWeights, vector<double>& foundScales,
                                          double detectThreshold = 0.0, Size winDetSize = Size(64, 128));
 
最后一个函数添加mean shift进行组合聚类,下面针对groupRectangles函数进行说明(前三个函数都调用了参数最多的第四个函数实现):
 
rectList:带组合的窗口,即作为输入又作为输出
rejectLevels:通过分类器的stage数,一般不小于stage总数-4,也就是weights
levelWeights:通过上述stage数的输出权重,也就是通过的stage数的所有node之和,里面即包含left_val又right_val,同一个node只包含其中的一个
groupThreshold:组合阈值,当没有输入rejectLevels的时候,当待合并的窗口数大于该阈值的时候才可能进行合并,否则放弃;当输入rejectLevels的时候,当前组合下通过检测的stage最大值数大于该阈值的时候才可能进行合并,否则放弃
eps:待合并的两个窗口的相关性,从矩形所在位置的像素差值考虑,当eps为0的时候不进行合并,直接返回
 
备注:本人使用的函数参数介解释
groupRectangles(dst, 1, 0.2);

//参数设置groupRectangles(CV_OUT CV_IN_OUT vector<Rect>& rectList, int groupThreshold, double eps=0.2);
// groupThreshold:组合阈值。待合并的窗口数大于该阈值的时候才可能进行合并,否则放弃 。若=n,表示(n+1)个及其以上才会进行组合。
//eps=0.2.值越大,越可能容易合并。eps=0 不进行合并

 
该函数的内部执行流程
1) 当组合阈值groupThreshold小于等于0的时候,如果输出weights,则weights中返回与rectList同样个数个1,函数直接返回,不进行合并操作
2) 调用partition函数对rectList中的矩形进行分类
    vector<int> labels;
    int nclasses = partition(rectList, labels, SimilarRects(eps));
其中nclasses表示组合类别,labels表示每个rect属于哪个类别的,相似度计算使用SimilarRects类
关于partition函数简介:http://docs.opencv.org/modules/core/doc/clustering.html#partition,实现不相交集合数据结构的类别
值得一提的是,该函数的调用必须输入不相交的计算方法,在groupRectangles函数中使用SimilarRects计算相似度,输入参数为eps,相似的矩形是要被分为同一类的
    SimilarRect中计算相似度的方法:
    inline bool operator()(const Rect& r1, const Rect& r2) const
    {
        // delta为最小长宽的eps倍
        double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;
        // 如果矩形的四个顶点的位置差别都小于delta,则表示相似的矩形
        return std::abs(r1.x - r2.x) <= delta &&
               std::abs(r1.y - r2.y) <= delta &&
               std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&
               std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;
    }
3) 组合分到同一类别的矩形并保存当前类别下通过stage的最大值以及最大的权重
    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]++;
    }
    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));
    }
 
    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];
     }
 
4) 按照groupThreshold合并规则,以及是否存在包含关系输出合并后的矩形
    for( i = 0; i < nclasses; i++ )
    {
        Rect r1 = rrects[i];
        int n1 = levelWeights ? rejectLevels[i] : rweights[i];
        double w1 = rejectWeights[i];
        // 合并的矩形数小于等于组合阈值不进行输出
        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 );
 
            // 当r1在r2的内部的时候,停止
            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;
        }
        // r1不在r2的内部时j才可能等于nclasses
        if( j == nclasses )
        {
            rectList.push_back(r1);
            if( weights )
                weights->push_back(n1);
            if( levelWeights )
                levelWeights->push_back(w1);
        }
    }
 
 

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted on 2015-10-14 09:52  realkate1  阅读(1353)  评论(0编辑  收藏  举报