OpenCV中CascadeClassifier类实现多尺度检测源码解析

首先给大家推荐一下我老师大神的人工智能教学网站。教学不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵黄段子!点这里可以跳转到网站 级联分类器检测类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; } }

点这里可以跳转到人工智能网站

发表评论