• opencv源码:cascadedetect


    级联分类器检测类CascadeClassifier,提供了两个重要的方法:

    CascadeClassifier cascade_classifier;
    cascade_classifier.load( cascade_dir + cascade_name );// 加载
    vector<Rect> object_rect;
    cascade_classifier.detectMultiScale( img1, object_rect, 1.1, min_win, 0|CASCADE_SCALE_IMAGE, Size(32,32) );// 识别

    头文件:objdetect.hpp,实现在cascadedetect.cpp中。

    CV_WRAP virtual void detectMultiScale( const Mat& image,
                                       CV_OUT vector<Rect>& objects,
                                       double scaleFactor=1.1,
                                       int minNeighbors=3, int flags=0,
                                       Size minSize=Size(),
                                       Size maxSize=Size() );

    代码的实现:

    void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
                                              double scaleFactor, int minNeighbors,
                                              int flags, Size minObjectSize, Size maxObjectSize)
    {
        vector<int> fakeLevels;
        vector<double> fakeWeights;
        detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
            minNeighbors, flags, minObjectSize, maxObjectSize, false );
    }

    fakeLevels是检测未通过层的级数,fakeWeights是未通过层的强分类器的输出,不使用时outputRejectLevels=false。

    //检测函数
    void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
                                              vector<int>& rejectLevels,
                                              vector<double>& levelWeights,
                                              double scaleFactor, int minNeighbors,
                                              int flags, Size minObjectSize, Size maxObjectSize,
                                              bool outputRejectLevels )
    {
        const double GROUP_EPS = 0.2;
    
        CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
    
        if( empty() )
            return;
    
        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;
        }
    
        objects.clear();
    
        if (!maskGenerator.empty()) {
            maskGenerator->initializeMask(image);
        }
    
    
        if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
            maxObjectSize = image.size();
    
        Mat grayImage = image;
        if( grayImage.channels() > 1 )
        {
            Mat temp;
            cvtColor(grayImage, temp, CV_BGR2GRAY);
            grayImage = temp;
        }
    
        Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
        vector<Rect> candidates;
    
        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, scaledImageSize.height - originalWindowSize.height );
    
            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;
    
            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;
            // 调用单尺度检测函数进行检测
            if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
                rejectLevels, levelWeights, outputRejectLevels ) )
                break;
        }
    
    
        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 );
        }
    }

    函数的工作:检测各个尺寸的图片,然后合并检测结果。具体单尺寸的检测见:

    bool CascadeClassifier::detectSingleScale( const Mat& image, int stripCount, Size processingRectSize,
                                               int stripSize, int yStep, double factor, vector<Rect>& candidates,
                                               vector<int>& levels, vector<double>& weights, bool outputRejectLevels )
    {
        // 计算当前图像的积分图
        if( !featureEvaluator->setImage( image, data.origWinSize ) )
            return false;
    
    #if defined (LOG_CASCADE_STATISTIC)
        logger.setImage(image);
    #endif
    
        Mat currentMask;
        if (!maskGenerator.empty()) {
            currentMask=maskGenerator->generateMask(image);
        }
    
        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
        {
            //CascadeClassifierInvoker函数的operator()实现具体的检测过程
             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() );
    
    #if defined (LOG_CASCADE_STATISTIC)
        logger.write();
    #endif
    
        return true;
    }

    进而:

    CascadeClassifierInvoker( CascadeClassifier& _cc, Size _sz1, int _stripSize, int _yStep, double _factor,
        vector<Rect>& _vec, vector<int>& _levels, vector<double>& _weights, bool outputLevels, const Mat& _mask, Mutex* _mtx)
    {
        classifier = &_cc;
        processingRectSize = _sz1;
        stripSize = _stripSize;
        yStep = _yStep;
        scalingFactor = _factor;
        rectangles = &_vec;
        rejectLevels = outputLevels ? &_levels : 0;
        levelWeights = outputLevels ? &_weights : 0;
        mask = _mask;
        mtx = _mtx;
    }
    
    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));
    
        int y1 = range.start * stripSize;
        int y2 = min(range.end * stripSize, processingRectSize.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;
                }
    
                double gypWeight;
                int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
    
    #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;
            }
        }
    }

    检测框的合并过程:

    void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps)
    {
        groupRectangles(rectList, groupThreshold, eps, 0, 0);
    }

    进而:

    void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, 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;
        }
    
        vector<int> labels;
        //对rectList中的矩形进行分类
        int nclasses = partition(rectList, labels, SimilarRects(eps));
    
        vector<Rect> rrects(nclasses);
        vector<int> rweights(nclasses, 0);
        vector<int> rejectLevels(nclasses, 0);
        vector<double> rejectWeights(nclasses, DBL_MIN);
        int i, j, nlabels = (int)labels.size();
        //组合分到同一类别的矩形并保存当前类别下通过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]++;
        }
        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];
            }
        }
    
        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();
        //按照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;
            }
    
            if( j == nclasses )
            {
                rectList.push_back(r1);
                if( weights )
                    weights->push_back(n1);
                if( levelWeights )
                    levelWeights->push_back(w1);
            }
        }
    }

    其中:

    class CV_EXPORTS SimilarRects
    {
    public:
        SimilarRects(double _eps) : eps(_eps) {}
        inline bool operator()(const Rect& r1, const Rect& r2) const
        {
            double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;
            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;
        }
        double eps;
    };

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

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

            http://docs.opencv.org/modules/core/doc/clustering.html#partition

  • 相关阅读:
    Error.prototype (Errors) – JavaScript 中文开发手册
    C 库函数 – isalnum()
    git diff-files (Git) – Git 中文开发手册
    Java面试题:如何在基于Java的Web项目中实现文件上传和下载?
    HTML onload 属性
    JavaScript setDate() 方法
    Linux fsconf命令
    HTML DOM Reset disabled 属性
    wcsstr (Strings) – C 中文开发手册
    HTML area shape 属性
  • 原文地址:https://www.cnblogs.com/houkai/p/4864569.html
Copyright © 2020-2023  润新知