人脸检测(九)--检测器源码分析

原文:

http://blog.csdn.net/xidianzhimeng/article/details/41851569

 

还有几篇蛮好:

http://blog.csdn.net/delltdk/article/details/23739441 总体描述,总结。

http://blog.csdn.net/tracyliang223/article/details/17379769,以HOG为例解释。

http://blog.csdn.net/naruto0001/article/details/8086745,以LBP为例解释。

http://blog.csdn.net/beerbuddys/article/details/40712957,以HARR为例解释。

级联分类器检测类CascadeClassifier,在2.4.5版本中使用Adaboost的方法+LBP、HOG、HAAR进行目标检测,加载的是使用traincascade进行训练的分类器

 

class CV_EXPORTS_W CascadeClassifier

{

public:

    CV_WRAP CascadeClassifier(); // 无参数构造函数,new自动调用该函数分配初试内存

    CV_WRAP CascadeClassifier( const string& filename ); // 带参数构造函数,参数为XML的绝对名称

    virtual ~CascadeClassifier(); // 析构函数,无需关心

 

    CV_WRAP virtual bool empty() const; // 是否导入参数,只创建了该对象而没有加载或者加载失败时都是空的

    CV_WRAP bool load( const string& filename ); // 加载分类器,参数为XML的绝对名称,函数内部调用read读取新格式的分类器,读取成功后直接返回,读取失败后调用cvLoad读取旧格式的分类器,读取成功返回true,否则返回false

    virtual bool read( const FileNode& node );   // load内部调用read解析XML中的内容,也可以自己创建节点然后调用Read即可,但是该函数只能读取新格式的分类器,不能读取旧格式的分类器

 

    // 多尺度检测函数

    CV_WRAP virtual void detectMultiScale( const Mat& image,        // 图像,cvarrtoMat实现IplImage转换为Mat,必须为8位,内部可自行转换为灰度图像

                                   CV_OUT vector<Rect>& objects,    // 输出矩形,注意vector不是线程安全的

                                   double scaleFactor=1.1,          // 缩放比例,必须大于1

                                   int minNeighbors=3,              // 合并窗口时最小neighbor,每个候选矩阵至少包含的附近元素个数

                                   int flags=0,                     // 检测标记,只对旧格式的分类器有效,与cvHaarDetectObjects的参数flags相同,默认为0,可能的取值为CV_HAAR_DO_CANNY_PRUNING(CANNY边缘检测)、CV_HAAR_SCALE_IMAGE(缩放图像)、CV_HAAR_FIND_BIGGEST_OBJECT(寻找最大的目标)、CV_HAAR_DO_ROUGH_SEARCH(做粗略搜索);如果寻找最大的目标就不能缩放图像,也不能CANNY边缘检测

                                   Size minSize=Size(),             // 最小检测目标

                                   Size maxSize=Size() );           // 最大检测目标

    // 最好不要在这里设置最大最小,可能会影响合并的效果,因此可以在检测完毕后自行判断结果是否满足要求

    CV_WRAP virtual void detectMultiScale( const Mat& image,

                                   CV_OUT vector<Rect>& objects,

                                   vector<int>& rejectLevels,

                                   vector<double>& levelWeights,

                                   double scaleFactor=1.1,

                                   int minNeighbors=3, int flags=0,

                                   Size minSize=Size(),

                                   Size maxSize=Size(),

                                   bool outputRejectLevels=false );

// 上述参数多了rejectLevels和levelWeights以及outputRejectLevels参数,只有在outputRejectLevels为true的时候才可能输出前两个参数

// 还有就是在使用旧分类器的时候必须设置flags为CV_HAAR_SCALE_IMAGE,可以通过haarcascade_frontalface_alt.xml检测人脸尝试

 

 

    bool isOldFormatCascade() const;        // 是否是旧格式的分类器

    virtual Size getOriginalWindowSize() const;    // 初始检测窗口大小,也就是训练的窗口

    int getFeatureType() const; // 获取特征类型

    bool setImage( const Mat& );    // 设置图像,计算图像的积分图

    virtual int runAt( Ptr<FeatureEvaluator>& feval, Point pt, double& weight ); // 计算某检测窗口是否为目标

    // 保存强分类器数据

    class Data

    {

    public:

        struct CV_EXPORTS DTreeNode // 节点

        {

            int featureIdx; // 对应的特征编号

            float threshold; // for ordered features only 节点阈值

            int left; // 左子树

            int right; // 右子树

        };

 

        struct CV_EXPORTS DTree // 弱分类器

        {

            int nodeCount; // 弱分类器中节点个数

        };

 

        struct CV_EXPORTS Stage // 强分类器

        {

            int first; // 在classifier中的起始位置

            int ntrees; // 该强分类器中的弱分类器数

            float threshold; // 强分类器阈值

        };

 

        bool read(const FileNode &node); // 读取强分类器

 

        bool isStumpBased;    // 是否只有树桩

 

        int stageType;      // BOOST,boostType:GAB、RAB等

        int featureType;    // HAAR、HOG、LBP

        int ncategories;    // maxCatCount,LBP为256,其余为0

        Size origWinSize;

 

        vector<Stage> stages;

        vector<DTree> classifiers;

        vector<DTreeNode> nodes;

        vector<float> leaves;

        vector<int> subsets;

    };

 

    Data data;

    Ptr<FeatureEvaluator> featureEvaluator;

    Ptr<CvHaarClassifierCascade> oldCascade;

// 关于mask这块参考《OpenCV目标检测之MaskGenerator》

public:

    class CV_EXPORTS MaskGenerator

    {

    public:

        virtual ~MaskGenerator() {}

        virtual cv::Mat generateMask(const cv::Mat& src)=0;

        virtual void initializeMask(const cv::Mat& /*src*/) {};

    };

    void setMaskGenerator(Ptr<MaskGenerator> maskGenerator);

    Ptr<MaskGenerator> getMaskGenerator();

 

    void setFaceDetectionMaskGenerator();

 

protected:

    Ptr<MaskGenerator> maskGenerator;

}

 

注意:当在不同的分类器之间切换的时候,需要手动释放,因为read内部没有释放上一次读取的分类器数据!

 

关于新旧格式的分类器参考《OpenCV存储解读之Adaboost分类器》

 

 

 

使用CascadeClassifier检测目标的过程

 

1) load分类器并调用empty函数检测是否load成功

// 读取stages

bool CascadeClassifier::Data::read(const FileNode &root)

{

    static const float THRESHOLD_EPS = 1e-5f;

 

    // load stage params

    string stageTypeStr = (string)root[CC_STAGE_TYPE];

    if( stageTypeStr == CC_BOOST )

        stageType = BOOST;

    else

        return false;

    printf("stageType: %s\n", stageTypeStr.c_str());

 

    string featureTypeStr = (string)root[CC_FEATURE_TYPE];

    if( featureTypeStr == CC_HAAR )

        featureType = FeatureEvaluator::HAAR;

    else if( featureTypeStr == CC_LBP )

        featureType = FeatureEvaluator::LBP;

    else if( featureTypeStr == CC_HOG )

        featureType = FeatureEvaluator::HOG;

 

    else

        return false;

    printf("featureType: %s\n", featureTypeStr.c_str());

 

    origWinSize.width = (int)root[CC_WIDTH];

    origWinSize.height = (int)root[CC_HEIGHT];

    CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );

 

    isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false;

    printf("stumpBased: %d\n", isStumpBased);

 

    // load feature params

    FileNode fn = root[CC_FEATURE_PARAMS];

    if( fn.empty() )

        return false;

    // LBP的maxCatCount=256,其余特征都等于0

    ncategories = fn[CC_MAX_CAT_COUNT]; // ncategories=256/0

    int subsetSize = (ncategories + 31)/32,// subsetSize=8/0 // 强制类型转换取整,不是四舍五入

        nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 ); //每组数值个数,nodeStep=11/4

    printf("subsetSize: %d, nodeStep: %d\n", subsetSize, nodeStep);

    // load stages

    fn = root[CC_STAGES];

    if( fn.empty() )

        return false;

 

    stages.reserve(fn.size());

    classifiers.clear();

    nodes.clear();

 

    FileNodeIterator it = fn.begin(), it_end = fn.end();

 

    for( int si = 0; it != it_end; si++, ++it )

    {

        FileNode fns = *it;

        Stage stage;

        stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;

        fns = fns[CC_WEAK_CLASSIFIERS];

        if(fns.empty())

            return false;

        stage.ntrees = (int)fns.size();

        stage.first = (int)classifiers.size();

        printf("stage %d: ntrees: %d, first: %d\n", si, stage.ntrees, stage.first);

        stages.push_back(stage);

        classifiers.reserve(stages[si].first + stages[si].ntrees);

 

        FileNodeIterator it1 = fns.begin(), it1_end = fns.end();

        for( ; it1 != it1_end; ++it1 ) // weak trees

        {

            FileNode fnw = *it1;

            FileNode internalNodes = fnw[CC_INTERNAL_NODES];

            FileNode leafValues = fnw[CC_LEAF_VALUES];

            if( internalNodes.empty() || leafValues.empty() )

                return false;

 

            // 弱分类器中的节点

            DTree tree;

            tree.nodeCount = (int)internalNodes.size()/nodeStep;

            classifiers.push_back(tree);

 

            nodes.reserve(nodes.size() + tree.nodeCount);

            leaves.reserve(leaves.size() + leafValues.size());

            if( subsetSize > 0 ) // 针对LBP

                subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);

 

            FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();

            // 保存每一个node

            for( ; internalNodesIter != internalNodesEnd; ) // nodes

            {

                DTreeNode node;

                node.left = (int)*internalNodesIter; ++internalNodesIter;

                node.right = (int)*internalNodesIter; ++internalNodesIter;

                node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;

                // 针对LBP,获取8个数值

                if( subsetSize > 0 )

                {

                    for( int j = 0; j < subsetSize; j++, ++internalNodesIter )

                        subsets.push_back((int)*internalNodesIter);

                    node.threshold = 0.f;

                }

                else

                {

                    node.threshold = (float)*internalNodesIter; ++internalNodesIter;

                }

                nodes.push_back(node);

            }

            // 保存叶子节点

            internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();

 

            for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves

                leaves.push_back((float)*internalNodesIter);

        }

    }

 

    return true;

}

// 读取stages与features

bool CascadeClassifier::read(const FileNode& root)

{

    // load stages

    if( !data.read(root) )

        return false;

 

    // load features,参考《图像特征->XXX特征之OpenCV-估计》

    featureEvaluator = FeatureEvaluator::create(data.featureType);

    FileNode fn = root[CC_FEATURES];

    if( fn.empty() )

        return false;

 

    return featureEvaluator->read(fn);

}

// 外部调用的函数

bool CascadeClassifier::load(const string& filename)

{

    oldCascade.release();

    data = Data();

    featureEvaluator.release();

    // 读取新格式的分类器

    FileStorage fs(filename, FileStorage::READ);

    if( !fs.isOpened() )

        return false;

 

    if( read(fs.getFirstTopLevelNode()) )

        return true;

 

    fs.release();

    // 读取新格式失败则读取旧格式的分类器

    oldCascade = Ptr<CvHaarClassifierCascade>((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));

    return !oldCascade.empty();

}

2) 调用detectMultiScale函数进行多尺度检测,该函数可以使用老分类器进行检测也可以使用新分类器进行检测

 

2.1 如果load的为旧格式的分类器则使用cvHaarDetectObjectsForROC进行检测,flags参数只对旧格式的分类器有效,参考《OpenCV函数解读之cvHaarDetectObjects》

    if( isOldFormatCascade() )

    {

        MemStorage storage(cvCreateMemStorage(0));

        CvMat _image = image;

        CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,

                                              minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );

        vector<CvAvgComp> vecAvgComp;

        Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);

        objects.resize(vecAvgComp.size());

        std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());

        return;

    }

2.2 新格式分类器多尺度检测

     for( double factor = 1; ; factor *= scaleFactor )

     {

        Size originalWindowSize = getOriginalWindowSize();

 

        Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );

        Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );

        Size processingRectSize( scaledImageSize.width-originalWindowSize.width + 1, scaledImageSize.height-originalWindowSize.height + 1 );

 

        if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )

            break;

        if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )

            break;

        if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )

            continue;

        // 缩放图像

        Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );

        resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );

        // 计算步长

        int yStep;

        if( getFeatureType() == cv::FeatureEvaluator::HOG )

        {

            yStep = 4;

        }

        else

        {

            yStep = factor > 2. ? 1 : 2;

        }

        // 并行个数以及大小,按照列进行并行处理

        int stripCount, stripSize;

        // 是否采用TBB进行优化

    #ifdef HAVE_TBB

        const int PTS_PER_THREAD = 1000;

        stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;

        stripCount = std::min(std::max(stripCount, 1), 100);

        stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;

    #else

        stripCount = 1;

        stripSize = processingRectSize.height;

    #endif

        // 调用单尺度检测函数进行检测

        if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,

            rejectLevels, levelWeights, outputRejectLevels ) )

            break;

    }

 

2.3 合并检测结果

    objects.resize(candidates.size());

    std::copy(candidates.begin(), candidates.end(), objects.begin());

 

    if( outputRejectLevels )

    {

        groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );

    }

    else

    {

        groupRectangles( objects, minNeighbors, GROUP_EPS );

    }

 

 

单尺度检测函数流程

2.2.1 根据所载入的特征计算积分图、积分直方图等

    // 计算当前图像的积分图,参考《图像特征->XXX特征之OpenCV-估计》

    if( !featureEvaluator->setImage( image, data.origWinSize ) )

        return false;

2.2.2 根据是否输出检测级数并行目标检测

    vector<Rect> candidatesVector;

    vector<int> rejectLevels;

    vector<double> levelWeights;

    Mutex mtx;

    if( outputRejectLevels )

    {

        parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,

            candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));

        levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );

        weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );

    }

    else

    {

         parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,

            candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));

    }

    candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );

 

CascadeClassifierInvoker函数的operator()实现具体的检测过程

    // 对于没有并行时range.start=0,range.end=1

    void operator()(const Range& range) const

    {

        Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();

 

        Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), 

                     cvRound(classifier->data.origWinSize.height * scalingFactor));

        // strip=processingRectSize.height

        int y1 = range.start * stripSize; // 0

        int y2 = min(range.end * stripSize, processingRectSize.height); // processSizeRect.height也就是可以处理的高度,已经减去窗口高度

        for( int y = y1; y < y2; y += yStep )

        {

            for( int x = 0; x < processingRectSize.width; x += yStep )

            {

                if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {

                    continue;

                }

                // result=1表示通过了所有的分类器 <=0表示失败的级数

                // gypWeight表示返回的阈值

                double gypWeight;

                int result = classifier->runAt(evaluator, Point(x, y), gypWeight);

// 输出LOG

#if defined (LOG_CASCADE_STATISTIC)

                logger.setPoint(Point(x, y), result);

#endif 

                // 当返回级数的时候可以最后三个分类器不通过

                if( rejectLevels )

                {

                    if( result == 1 )

                        result = -(int)classifier->data.stages.size();

                    // 可以最后三个分类器不通过

                    if( classifier->data.stages.size() + result < 4 )

                    {

                        mtx->lock();

                        rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));

                        rejectLevels->push_back(-result);

                        levelWeights->push_back(gypWeight);

                        mtx->unlock();

                    }

                }

                // 不返回级数的时候通过所有的分类器才保存起来

                else if( result > 0 )

                {

                    mtx->lock();

                    rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),

                                               winSize.width, winSize.height));

                    mtx->unlock();

                }

                // 如果一级都没有通过那么加大搜索步长

                if( result == 0 )

                    x += yStep;

            }

        }

    }

 

runAt函数实现某一检测窗口的检测

int CascadeClassifier::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )

{

    CV_Assert( oldCascade.empty() );

 

    assert( data.featureType == FeatureEvaluator::HAAR ||

            data.featureType == FeatureEvaluator::LBP ||

            data.featureType == FeatureEvaluator::HOG );

    // 设置某一点处的特征,参考《图像特征->XXX特征之OpenCV-估计》

    if( !evaluator->setWindow(pt) )

        return -1;

    // 如果为树桩,没有树枝

    if( data.isStumpBased )

    {

        if( data.featureType == FeatureEvaluator::HAAR )

            return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );

        else if( data.featureType == FeatureEvaluator::LBP )

            return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );

        else if( data.featureType == FeatureEvaluator::HOG )

            return predictOrderedStump<HOGEvaluator>( *this, evaluator, weight );

        else

            return -2;

    }

    // 每个弱分类器不止一个node

    else

    {

        if( data.featureType == FeatureEvaluator::HAAR )

            return predictOrdered<HaarEvaluator>( *this, evaluator, weight );

        else if( data.featureType == FeatureEvaluator::LBP )

            return predictCategorical<LBPEvaluator>( *this, evaluator, weight );

        else if( data.featureType == FeatureEvaluator::HOG )

            return predictOrdered<HOGEvaluator>( *this, evaluator, weight );

        else

            return -2;

    }

}

 

predictOrdered*函数实现判断当前检测窗口的判断

// HAAR与HOG特征的多node检测

template<class FEval>

inline int predictOrdered( CascadeClassifier& cascade, Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )

{

    int nstages = (int)cascade.data.stages.size();

    int nodeOfs = 0, leafOfs = 0;

    FEval& featureEvaluator = (FEval&)*_featureEvaluator;

    float* cascadeLeaves = &cascade.data.leaves[0];

    CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];

    CascadeClassifier::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];

    CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];

 

    // 遍历每个强分类器

    for( int si = 0; si < nstages; si++ )

    {

        CascadeClassifier::Data::Stage& stage = cascadeStages[si];

        int wi, ntrees = stage.ntrees;

        sum = 0;

        // 遍历每个弱分类器

        for( wi = 0; wi < ntrees; wi++ )

        {

            CascadeClassifier::Data::DTree& weak = cascadeWeaks[stage.first + wi];

            int idx = 0, root = nodeOfs;

            // 遍历每个节点

            do

            {

                // 选择一个node:root和idx初始化为0,即第一个node

                CascadeClassifier::Data::DTreeNode& node = cascadeNodes[root + idx];

                // 计算当前node特征池编号下的特征值

                double val = featureEvaluator(node.featureIdx);

                // 如果val小于node阈值则选择左子树,否则选择右子树

                idx = val < node.threshold ? node.left : node.right;

            } while( idx > 0 );

            // 累加最终的叶子节点

            sum += cascadeLeaves[leafOfs - idx];

            nodeOfs += weak.nodeCount;

            leafOfs += weak.nodeCount + 1;

        }

        // 判断所有叶子节点累加和是否小于强分类器阈值,小于强分类器阈值则失败

        if( sum < stage.threshold )

            return -si;

    }

    // 通过了所有的强分类器返回1,否则返回失败的分类器

    return 1;

}

 

// LBP特征的多node检测

template<class FEval>

inline int predictCategorical( CascadeClassifier& cascade, Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )

{

    int nstages = (int)cascade.data.stages.size();

    int nodeOfs = 0, leafOfs = 0;

    FEval& featureEvaluator = (FEval&)*_featureEvaluator;

    size_t subsetSize = (cascade.data.ncategories + 31)/32;

    int* cascadeSubsets = &cascade.data.subsets[0];

    float* cascadeLeaves = &cascade.data.leaves[0];

    CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];

    CascadeClassifier::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];

    CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];

 

    for(int si = 0; si < nstages; si++ )

    {

        CascadeClassifier::Data::Stage& stage = cascadeStages[si];

        int wi, ntrees = stage.ntrees;

        sum = 0;

 

        for( wi = 0; wi < ntrees; wi++ )

        {

            CascadeClassifier::Data::DTree& weak = cascadeWeaks[stage.first + wi];

            int idx = 0, root = nodeOfs;

            do

            {

                CascadeClassifier::Data::DTreeNode& node = cascadeNodes[root + idx];

                // c为0-255之间的数

                int c = featureEvaluator(node.featureIdx);

                // 获取当前node的subset头位置

                const int* subset = &cascadeSubsets[(root + idx)*subsetSize]; // LBP:subsetSize=8

                // 判断选择左子树还是右子树

                idx = (subset[c>>5] & (1 << (c & 31))) ? node.left : node.right;

                // c>>5表示将c右移5位,选择高3位,0-7之间

                // c&31表示低5位,1<<(c&31)选择低5位后左移1位

                // 将上面的数按位与,如果最后结果不为0表示选择左子树,否则选择右子树

            }

            while( idx > 0 );

            sum += cascadeLeaves[leafOfs - idx];

            nodeOfs += weak.nodeCount;

            leafOfs += weak.nodeCount + 1;

        }

        if( sum < stage.threshold )

            return -si;

    }

    return 1;

}

 

// HAAR与HOG特征的单node检测

template<class FEval>

inline int predictOrderedStump( CascadeClassifier& cascade, Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )

{

    int nodeOfs = 0, leafOfs = 0;

    FEval& featureEvaluator = (FEval&)*_featureEvaluator;

    float* cascadeLeaves = &cascade.data.leaves[0];

    CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];

    CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];

 

    int nstages = (int)cascade.data.stages.size();

    // 遍历每个强分类器

    for( int stageIdx = 0; stageIdx < nstages; stageIdx++ )

    {

        CascadeClassifier::Data::Stage& stage = cascadeStages[stageIdx];

        sum = 0.0;

 

        int ntrees = stage.ntrees;

        // 遍历每个弱分类器

        for( int i = 0; i < ntrees; i++, nodeOfs++, leafOfs+= 2 )

        {

            CascadeClassifier::Data::DTreeNode& node = cascadeNodes[nodeOfs];

            double value = featureEvaluator(node.featureIdx);

            sum += cascadeLeaves[ value < node.threshold ? leafOfs : leafOfs + 1 ];

        }

 

        if( sum < stage.threshold )

            return -stageIdx;

    }

 

    return 1;

}

// LBP特征的单node检测

template<class FEval>

inline int predictCategoricalStump( CascadeClassifier& cascade, Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )

{

    int nstages = (int)cascade.data.stages.size();

    int nodeOfs = 0, leafOfs = 0;

    FEval& featureEvaluator = (FEval&)*_featureEvaluator;

    size_t subsetSize = (cascade.data.ncategories + 31)/32;

    int* cascadeSubsets = &cascade.data.subsets[0];

    float* cascadeLeaves = &cascade.data.leaves[0];

    CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];

    CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];

 

#ifdef HAVE_TEGRA_OPTIMIZATION

    float tmp = 0; // float accumulator -- float operations are quicker

#endif

    for( int si = 0; si < nstages; si++ )

    {

        CascadeClassifier::Data::Stage& stage = cascadeStages[si];

        int wi, ntrees = stage.ntrees;

#ifdef HAVE_TEGRA_OPTIMIZATION

        tmp = 0;

#else

        sum = 0;

#endif

 

        for( wi = 0; wi < ntrees; wi++ )

        {

            CascadeClassifier::Data::DTreeNode& node = cascadeNodes[nodeOfs];

            int c = featureEvaluator(node.featureIdx);

            const int* subset = &cascadeSubsets[nodeOfs*subsetSize];

#ifdef HAVE_TEGRA_OPTIMIZATION

            tmp += cascadeLeaves[ subset[c>>5] & (1 << (c & 31)) ? leafOfs : leafOfs+1];

#else

            sum += cascadeLeaves[ subset[c>>5] & (1 << (c & 31)) ? leafOfs : leafOfs+1];

#endif

            nodeOfs++;

            leafOfs += 2;

        }

#ifdef HAVE_TEGRA_OPTIMIZATION

        if( tmp < stage.threshold ) {

            sum = (double)tmp;

            return -si;

        }

#else

        if( sum < stage.threshold )

            return -si;

#endif

    }

 

#ifdef HAVE_TEGRA_OPTIMIZATION

    sum = (double)tmp;

#endif

 

    return 1;

}

}

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值