现在的位置: 首页 > 综合 > 正文

cvHaarDetectObjects

2013年02月02日 ⁄ 综合 ⁄ 共 10736字 ⁄ 字号 评论关闭

OpenCV的人脸检测主要是调用训练好的cascade(Haar分类器)来进行模式匹配

cvHaarDetectObjects,先将图像灰度化,根据传入参数判断是否进行canny边缘处理(默认不使用),再进行匹配。匹配后收集找出的匹配块,过滤噪声,计算相邻个数如果超过了规定值(传入的min_neighbors)就当成输出结果,否则删去。

匹配循环:将匹配分类器放大scale(传入值)倍,同时原图缩小scale倍,进行匹配,直到匹配分类器的大小大于原图,则返回匹配结果。匹配的时候调用cvRunHaarClassifierCascade来进行匹配,将所有结果存入CvSeq* Seq (可动态增长元素序列),将结果传给cvHaarDetectObjects。

cvRunHaarClassifierCascade函数整体是根据传入的图像和cascade来进行匹配。并且可以根据传入的cascade类型不同(树型、stump(不完整的树)或其他的),进行不同的匹配方式。

函数 cvRunHaarClassifierCascade 用于对单幅图片的检测。在函数调用前首先利用 cvSetImagesForHaarClassifierCascade设定积分图和合适的比例系数 (=> 窗口尺寸)。当分析的矩形框全部通过级联分类器每一层的时返回正值(这是一个候选目标),否则返回0或负值。

为了了解OpenCV人脸检测中寻找匹配图像的详细过程,就把cvHaarDetectObjects和cvRunHaarClassifierCascade的源文件详细看了一遍,并打上了注释。方便大家阅读。

附cvHaarDetectObjects代码:

CV_IMPL CvSeq*
cvHaarDetectObjects( const CvArr* _img, 
                     CvHaarClassifierCascade* cascade,
                     CvMemStorage* storage, double scale_factor,
                     int min_neighbors, int flags, CvSize min_size )
{
    int split_stage = 2;
     CvMat stub, *img = (CvMat*)_img;        //CvMat多通道矩阵  *img=_img指针代换传入图
    CvMat *temp = 0, *sum = 0, *tilted = 0, *sqsum = 0, *norm_img = 0, *sumcanny = 0, *img_small = 0;
    CvSeq* seq = 0;
    CvSeq* seq2 = 0;           //CvSeq可动态增长元素序列
    CvSeq* idx_seq = 0;
    CvSeq* result_seq = 0;
    CvMemStorage* temp_storage = 0;
    CvAvgComp* comps = 0;
    int i;   
#ifdef _OPENMP
    CvSeq* seq_thread[CV_MAX_THREADS] = {0};
    int max_threads = 0;
#endif   
    CV_FUNCNAME( “cvHaarDetectObjects” ); 
    __BEGIN__; 
    double factor;
    int npass = 2, coi;                        //npass=2
    int do_canny_pruning = flags & CV_HAAR_DO_CANNY_PRUNING;                 //true做canny边缘处理 
    if( !CV_IS_HAAR_CLASSIFIER(cascade) )
        CV_ERROR( !cascade ? CV_StsNullPtr : CV_StsBadArg, “Invalid classifier cascade” ); 
    if( !storage )
        CV_ERROR( CV_StsNullPtr, “Null storage pointer” ); 
    CV_CALL( img = cvGetMat( img, &stub, &coi ));
    if( coi )
        CV_ERROR( CV_BadCOI, “COI is not supported” );       //一些出错代码 
    if( CV_MAT_DEPTH(img->type) != CV_8U )
        CV_ERROR( CV_StsUnsupportedFormat, “Only 8-bit images are supported” ); 
    CV_CALL( temp = cvCreateMat( img->rows, img->cols, CV_8UC1 ));
    CV_CALL( sum = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 ));
    CV_CALL( sqsum = cvCreateMat( img->rows + 1, img->cols + 1, CV_64FC1 ));
    CV_CALL( temp_storage = cvCreateChildMemStorage( storage )); 
#ifdef _OPENMP
    max_threads = cvGetNumThreads();
    for( i = 0; i < max_threads; i++ )
    {   CvMemStorage* temp_storage_thread;
        CV_CALL( temp_storage_thread = cvCreateMemStorage(0));                 //CV_CALL就是运行,假如出错就报错。
        CV_CALL( seq_thread[i] = cvCreateSeq( 0, sizeof(CvSeq),                //CvSeq可动态增长元素序列 
        sizeof(CvRect), temp_storage_thread ));
    }
#endif
     if( !cascade->hid_cascade )
        CV_CALL( icvCreateHidHaarClassifierCascade(cascade) ); 
    if( cascade->hid_cascade->has_tilted_features )
        tilted = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );         //多通道矩阵 图像长宽+1 4通道 
    seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvRect), temp_storage );        //创建序列seq  矩形
    seq2 = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), temp_storage );    //创建序列seq2  矩形和邻近
    result_seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), storage );   //创建序列result_seq  矩形和邻近 
    if( min_neighbors == 0 )
        seq = result_seq; 
    if( CV_MAT_CN(img->type) > 1 )
    {   cvCvtColor( img, temp, CV_BGR2GRAY );         //img转为灰度
        img = temp;                                                                                                                                
    }   
    if( flags & CV_HAAR_SCALE_IMAGE )                //flag && 匹配图
    {   CvSize win_size0 = cascade->orig_window_size;            //CvSize win_size0为分类器的原始大小
        int use_ipp = cascade->hid_cascade->ipp_stages != 0 &&    icvApplyHaarClassifier_32s32f_C1R_p != 0;     //IPP相关函数 
        if( use_ipp )
            CV_CALL( norm_img = cvCreateMat( img->rows, img->cols, CV_32FC1 ));           //图像的矩阵化 4通道.
        CV_CALL( img_small = cvCreateMat( img->rows + 1, img->cols + 1, CV_8UC1 ));       //小图矩阵化 单通道 长宽+1 
        for( factor = 1; ; factor *= scale_factor )                                      
//成scale_factor倍数匹配
        {  int positive = 0;
            int x, y;
            CvSize win_size = { cvRound(win_size0.width*factor),   cvRound(win_size0.height*factor) };    
//winsize         分类器行列(扩大factor倍)        
            CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor ) };     //sz 图像行列(缩小factor倍)              
三个Cvsize
            CvSize sz1 = { sz.width – win_size0.width, sz.height – win_size0.height };    //sz1 图像减分类器行列
            CvRect rect1 = { icv_object_win_border, icv_object_win_border,
                win_size0.width – icv_object_win_border*2,                                //icv_object_win_border
(int) 初始值=1
                win_size0.height – icv_object_win_border*2 };                                 
//矩形框rect1
            CvMat img1, sum1, sqsum1, norm1, tilted1, mask1;                             
//多通道矩阵
            CvMat* _tilted = 0; 
            if( sz1.width <= 0 || sz1.height <= 0 )                                      
//图片宽或高小于分类器–>跳出
                break;
            if( win_size.width < min_size.width || win_size.height < min_size.height )    //分类器高或宽小于给定的mini_size的高或宽–>继续
                continue;
//CV_8UC1见定义.
//#define CV_MAKETYPE(depth,cn) ((depth) + (((cn)-1) << CV_CN_SHIFT))   
//深度+(cn-1)左移3位   depth,depth+8,depth+16,depth+24.
            img1 = cvMat( sz.height, sz.width, CV_8UC1, img_small->data.ptr );            //小图的矩阵化 img1 单通道    
            sum1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, sum->data.ptr );             //长宽+1 4通道8位           
多通道矩阵
            sqsum1 = cvMat( sz.height+1, sz.width+1, CV_64FC1, sqsum->data.ptr );         //长宽+1 4通道16位
            if( tilted )
            {
                tilted1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, tilted->data.ptr );   //长宽+1 4通道8位
                _tilted = &tilted1;                                                                 
//长宽+1 4通道8位
            }
            norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, norm_img ? norm_img->data.ptr : 0 );  //norm1 图像 减 分类器行列 4通道
            mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr );                     
//mask1 灰度图
 
            cvResize( img, &img1, CV_INTER_LINEAR );                                     
//img双线性插值 输出到img1
            cvIntegral( &img1, &sum1, &sqsum1, _tilted );                                                                 
//计算积分图像
 
            if( use_ipp && icvRectStdDev_32s32f_C1R_p( sum1.data.i, sum1.step,
                sqsum1.data.db, sqsum1.step, norm1.data.fl, norm1.step, sz1, rect1 ) < 0 )
                use_ipp = 0; 
            if( use_ipp )                                                                                
//如果ipp=true   (intel视频处理加速等的函数库)
            {
                positive = mask1.cols*mask1.rows;                                                                                 
//mask1长乘宽–>positive
                cvSet( &mask1, cvScalarAll(255) );                                                                                 
//mask1赋值为255
                for( i = 0; i < cascade->count; i++ )
                {
                    if( icvApplyHaarClassifier_32s32f_C1R_p(sum1.data.i, sum1.step,
                        norm1.data.fl, norm1.step, mask1.data.ptr, mask1.step,
                        sz1, &positive, cascade->hid_cascade->stage_classifier[i].threshold,
                        cascade->hid_cascade->ipp_stages[i]) < 0 )
                    {
                        use_ipp = 0;                                                                        
//ipp=false;
                        break;
                    }
                    if( positive <= 0 )
                        break;
                }
            }           
            if( !use_ipp )                             
//如果ipp=false
            {
                cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, 0, 1. );
                for( y = 0, positive = 0; y < sz1.height; y++ )
                    for( x = 0; x < sz1.width; x++ )
                    {
                        mask1.data.ptr[mask1.step*y + x] =
                            cvRunHaarClassifierCascade( cascade, cvPoint(x,y), 0 ) >
0;   //匹配图像.
                        positive += mask1.data.ptr[mask1.step*y + x];
                    }
            } 
            if( positive > 0 )
            {
                for( y = 0; y < sz1.height; y++ )
                    for( x = 0; x < sz1.width; x++ )
                        if( mask1.data.ptr[mask1.step*y + x] != 0 )
                        {
                            CvRect obj_rect = { cvRound(y*factor), cvRound(x*factor),    
                                               
win_size.width, win_size.height };
                            cvSeqPush( seq, &obj_rect );                                       
//将匹配块放到seq中
                        }
            }
        }
    }
    else                //!(flag && 匹配图)
    {
        cvIntegral( img, sum, sqsum, tilted );   
        if( do_canny_pruning )
        {
            sumcanny = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );                 
//如果 做canny边缘检测
            cvCanny( img, temp, 0, 50, 3 );
            cvIntegral( temp, sumcanny );
        }   
        if( (unsigned)split_stage >= (unsigned)cascade->count ||
            cascade->hid_cascade->is_tree )                                                                                                                
        {
            split_stage = cascade->count;
            npass = 1;
        } 
        for( factor = 1; factor*cascade->orig_window_size.width < img->cols – 10 &&                                //匹配
                         factor*cascade->orig_window_size.height < img->rows – 10;
             factor *= scale_factor )
        {
            const double ystep = MAX( 2, factor );
            CvSize win_size = { cvRound( cascade->orig_window_size.width * factor ),
                                cvRound( cascade->orig_window_size.height
* factor )};
            CvRect equ_rect = { 0, 0, 0, 0 };
            int *p0 = 0, *p1 = 0, *p2 = 0, *p3 = 0;
            int *pq0 = 0, *pq1 = 0, *pq2 = 0, *pq3 = 0;
            int pass, stage_offset = 0;
            int stop_height = cvRound((img->rows – win_size.height) / ystep); 
            if( win_size.width < min_size.width || win_size.height < min_size.height )        //超边跳出
                continue; 
            cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor );   //匹配
            cvZero( temp );                         //清空temp数组
 
            if( do_canny_pruning )                //canny边缘检测
            {
                equ_rect.x = cvRound(win_size.width*0.15);
                equ_rect.y = cvRound(win_size.height*0.15);
                equ_rect.width = cvRound(win_size.width*0.7);
                equ_rect.height = cvRound(win_size.height*0.7); 
                p0 = (int*)(sumcanny->data.ptr + equ_rect.y*sumcanny->step) + equ_rect.x;
                p1 = (int*)(sumcanny->data.ptr + equ_rect.y*sumcanny->step)
                            + equ_rect.x + equ_rect.width;
                p2 = (int*)(sumcanny->data.ptr + (equ_rect.y + equ_rect.height)*sumcanny->step) + equ_rect.x;
                p3 = (int*)(sumcanny->data.ptr + (equ_rect.y + equ_rect.height)*sumcanny->step)
                            + equ_rect.x + equ_rect.width; 
                pq0 = (int*)(sum->data.ptr + equ_rect.y*sum->step) + equ_rect.x;
                pq1 = (int*)(sum->data.ptr + equ_rect.y*sum->step)
                            + equ_rect.x +

抱歉!评论已关闭.