当前位置: 首页 > 工具软件 > Cascade > 使用案例 >

CascadeClassifier

索曾琪
2023-12-01


1、收集相关的资料

1.1、采用opencv_cascadetrain进行训练的步骤及注意事项

1.2、将haartraining 输出的数据stagen.xml 合并为 cascade.xml 文件 (使用了 convert_cascade.c)   48x48 注意中间是x(xyz的x)

1.3、 haartraing : sources\apps\目录下



---------------------------------------------------------------------------------------------------------------------------------------------------------------

void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
                                          double scaleFactor, int minNeighbors,
                                          int flags, Size minSize )
{
    CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );// 尺度比较大约1
    
    if( empty() )
        return;

    if( !oldCascade.empty() )
    {
        MemStorage storage(cvCreateMemStorage(0));
        CvMat _image = image;
        CvSeq* _objects = cvHaarDetectObjects( &_image, oldCascade, storage, scaleFactor,
                                              minNeighbors, flags, minSize );
        vector<CvAvgComp> vecAvgComp;
        Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
        objects.resize(vecAvgComp.size());
        std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
        return;
    }
    
    objects.clear();
    
    Mat img = image, imgbuf(image.rows+1, image.cols+1, CV_8U);
    
    if( img.channels() > 1 )// 若是多通道,转成灰度图像进行处理。即后续只支持灰度图像
    {
        Mat temp;
        cvtColor(img, temp, CV_BGR2GRAY);
        img = temp;// img贯彻后面,若是单通道直接指向image,否则指向变换后的灰度图像
    }
    
    int maxNumThreads = 1;
#ifdef _OPENMP
    maxNumThreads = cv::getNumThreads();	//这个线程数用于将Vector分成多个,每个用于一个独立线程,方便后面合并
#endif
    vector<vector<Rect> > rects( maxNumThreads );
    vector<Rect>* rectsPtr = &rects[0];
    vector<Ptr<FeatureEvaluator> > fevals( maxNumThreads );
    fevals[0] = feval;// feval特征评估器指针,用于计算和存储特征
    Ptr<FeatureEvaluator>* fevalsPtr = &fevals[0];

    for( double factor = 1; ; factor *= scaleFactor )//一个尺度一个尺度计算
    {
        int stripCount, stripSize;
        Size winSize( cvRound(origWinSize.width*factor), cvRound(origWinSize.height*factor) );//滑动窗口大小
        Size sz( cvRound( img.cols/factor ), cvRound( img.rows/factor ) );//图像按这个系数缩放后大小
        Size sz1( sz.width - origWinSize.width, sz.height - origWinSize.height );//图像大小-滑动窗口大小,用于计算滑动次数
        
        if( sz1.width <= 0 || sz1.height <= 0 )//若是成立,说明图像大小比窗口还小
            break;
        if( winSize.width < minSize.width || winSize.height < minSize.height )//滑动窗口是否满足最小尺寸?
            continue;
        
        int yStep = factor > 2. ? 1 : 2;// 加大步长:在小尺度时,步长也小;在大尺度是,步长也加大
        if( maxNumThreads > 1 )
        {
            stripCount = max(min(sz1.height/yStep, maxNumThreads*3), 1);//sz1.height/yStep:可以垂直滑动窗口几次
            stripSize = (sz1.height + stripCount - 1)/stripCount;//stripCount:代表分了多少份,stripSize:每份大小
            stripSize = (stripSize/yStep)*yStep; //? 可以理解取整吗??
        }
        else
        {
            stripCount = 1;
            stripSize = sz1.height;
        }

        Mat img1( sz, CV_8U, imgbuf.data );
        resize( img, img1, sz, 0, 0, CV_INTER_LINEAR );//将图像缩小
		//Ptr<FeatureEvaluator> feval;是一个成员变量指针,指向了特征评估器
		//setImage()方法根据输入图像计算其特征(Haar、LBP)
        if( !feval->setImage( img1, origWinSize ) )// 计算Haar特征
            break;
        for( int i = 1; i < maxNumThreads; i++ )// Ptr<FeatureEvaluator> feval;
            fevalsPtr[i] = feval->clone();// clone()复制全特征,并返回指针
        
#ifdef _OPENMP
#pragma omp parallel for num_threads(maxNumThreads) schedule(dynamic)
#endif
        for( int i = 0; i < stripCount; i++ )//此处可以启动多线程
        {
            int threadIdx = cv::getThreadNum();
            int y1 = i*stripSize, y2 = (i+1)*stripSize; //多线程是按高度进行分解的任务,每个线程负责一个区域
            if( i == stripCount - 1 || y2 > sz1.height )
                y2 = sz1.height;
            Size ssz(sz1.width, y2 - y1);

            for( int y = y1; y < y2; y += yStep ) // 先按行 //垂直滑动次数,为什么从y1开始,到y2结束呢?
                for( int x = 0; x < ssz.width; x += yStep )//再按列(水平滑动);水平滑动次数
                {	//runAt():The function returns 1 if the cascade classifier detects an object in the given location.
                    int r = runAt(fevalsPtr[threadIdx], Point(x,y));//runAt():Runs the detector at the specified point.
                    if( r > 0 )
                        rectsPtr[threadIdx].push_back(Rect(cvRound(x*factor), cvRound(y*factor),
                                               winSize.width, winSize.height));
                    else if( r == 0 )
                        x += yStep;
                }
        }
    }
    for( vector< vector<Rect> >::const_iterator it = rects.begin(); it != rects.end(); it++ )//设计2层Vector的目的是为了支持多线程
        objects.insert( objects.end(), it->begin(), it->end() );//将两层Vector合并到一层Vector中
    groupRectangles( objects, minNeighbors, 0.2 );//合并重叠的矩形区域
}    


 类似资料:

相关阅读

相关文章

相关问答